2016-04-19 12 views
68

減速機自体にアクションをディスパッチすることは可能ですか?私はプログレスバーとオーディオ要素を持っています。目標は、オーディオエレメントで時間が更新されるときにプログレスバーを更新することです。しかし、私はontimeupdateイベントハンドラをどこに置くべきか、ontimeupdateのコールバックにアクションをディスパッチしてプログレスバーを更新する方法を知らない。ここに私のコードは次のとおりです。減速中にアクションを派遣レデューサーでアクションをディスパッチできますか?

//reducer 

const initialState = { 
    audioElement: new AudioElement('test.mp3'), 
    progress: 0.0 
} 

initialState.audioElement.audio.ontimeupdate =() => { 
    console.log('progress', initialState.audioElement.currentTime/initialState.audioElement.duration); 
    //how to dispatch 'SET_PROGRESS_VALUE' now? 
}; 


const audio = (state=initialState, action) => { 
    switch(action.type){ 
     case 'SET_PROGRESS_VALUE': 
      return Object.assign({}, state, {progress: action.progress}); 
     default: return state; 
    } 

} 

export default audio; 
+0

を '' AudioElement''は何ですか?それは状態の何かであってはならないようです。 – mtaube

+0

これは、オーディオオブジェクトを保持するES6プレーンクラス(反応なし)です。それが状態にない場合、私はどのように再生/停止を制御するでしょうか、スキップしますか? – vacetahanna

+1

あなたはreduxのサガを見てみたいかもしれません – Tyrsius

答えて

57

はアンチパターンです。あなたのレデューサーは副作用がなく、アクションペイロードを消化して新しい状態オブジェクトを返すだけです。リデューサを追加してリデューサ内にアクションをディスパッチすると、連鎖アクションやその他の副作用が発生する可能性があります。

初期化されたAudioElementクラスのようなサウンドとイベントリスナーは、状態ではなくコンポーネント内に属しています。イベントリスナー内でアクションをディスパッチすると、状態でprogressが更新されます。

AudioElementクラスオブジェクトを新しいReactコンポーネントで初期化することも、そのクラスをReactコンポーネントに変換することもできます。

class MyAudioPlayer extends React.Component { 
    constructor(props) { 
    super(props); 

    this.player = new AudioElement('test.mp3'); 

    this.player.audio.ontimeupdate = this.updateProgress; 
    } 

    updateProgress() { 
    // Dispatch action to reducer with updated progress. 
    // You might want to actually send the current time and do the 
    // calculation from within the reducer. 
    this.props.updateProgress(); 
    } 

    render() { 
    // Render the audio player controls, progress bar, whatever else 
    return <p>Progress: {this.props.progress}</p>; 
    } 
} 

class MyContainer extends React.Component { 
    render() { 
    return <MyAudioPlayer updateProgress={this.props.updateProgress} /> 
    } 
} 

function mapStateToProps (state) { return {}; } 

return connect(mapStateToProps, { 
    updateProgressAction 
})(MyContainer); 

あなたが直接派遣を呼び出す必要はありませんので、updateProgressActionが自動的にdispatchでラップされていることに注意してください。

+0

非常に説明のためにありがとう!しかし、私はまだディスパッチャーにアクセスする方法を知らない。私はいつもreact-reduxのconnectメソッドを使っていました。 updateProgressメソッドで呼び出す方法はわかりません。または、ディスパッチャを取得する別の方法があります。多分小道具で?ありがとうございます – vacetahanna

+0

問題ありません。アクションを、 '' react-redux''で ''接続する ''親コンテナから '' MyAudioPlayer''コンポーネントに渡すことができます。ここで '' mapDispatchToProps''を使ってそれを行う方法を確認してください:https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options – mtaube

+0

編集をもっと見るヒント。 – mtaube

9

redux-sagaのようなライブラリをお試しください。これは、非常にシーケンスの非同期関数、アクションをオフに、遅延などを使用してきれいな方法を可能にします。それは非常に強力です!

+0

redux-sagaで 'レデューサー内の別のディスパッチをスケジュールする'方法を指定できますか? – XPD

+1

@XPDあなたが達成したいことをもう少し説明できますか?あなたが他のアクションをディスパッチするためにリデューサーアクションを使用しようとしている場合、あなたはredux-sagaのようなものなしではできません。 – chandlervdw

+0

たとえば、アイテムの一部をロードしたアイテムストアがあるとします。アイテムは遅れて読み込まれます。アイテムにサプライヤがあるとします。サプライヤもまた遅れて積み重ねられた。この場合、サプライヤがロードされていないアイテムが存在する可能性があります。アイテムリデューサで、まだロードされていないアイテムに関する情報を取得する必要がある場合は、リデューサを介してサーバーからデータを再度ロードする必要があります。その場合、redux-sagaは還元剤の中でどのように役立ちますか? – XPD

42

レデューサーが終了する前に別のディスパッチを開始すると、レデューサーの終了時に受け取った状態が、レデューサーの終了時に現在のアプリケーション状態にならなくなるため、逆パターンとなります。しかし、レデューサー内から別の発送をスケジュールすることは、アンチパターンではありません。実際、これがElm言語の機能であり、ReduxがElmアーキテクチャをJavaScriptにもたらす試みであることはご承知のとおりです。

ここには、すべての操作にプロパティasyncDispatchが追加されるミドルウェアがあります。あなたのレデューサーが終了し、新しいアプリケーションの状態を返すと、asyncDispatchはあなたがそれに与えた何らかのアクションでstore.dispatchをトリガーします。

// This middleware will just add the property "async dispatch" 
// to actions with the "async" propperty set to true 
const asyncDispatchMiddleware = store => next => action => { 
    let syncActivityFinished = false; 
    let actionQueue = []; 

    function flushQueue() { 
    actionQueue.forEach(a => store.dispatch(a)); // flush queue 
    actionQueue = []; 
    } 

    function asyncDispatch(asyncAction) { 
    actionQueue = actionQueue.concat([asyncAction]); 

    if (syncActivityFinished) { 
     flushQueue(); 
    } 
    } 

    const actionWithAsyncDispatch = 
    Object.assign({}, action, { asyncDispatch }); 

    next(actionWithAsyncDispatch); 
    syncActivityFinished = true; 
    flushQueue(); 
}; 

今、あなたの減速がこれを行うことができます:

function reducer(state, action) { 
    switch (action.type) { 
    case "fetch-start": 
     fetch('wwww.example.com') 
     .then(r => r.json()) 
     .then(r => action.asyncDispatch({ type: "fetch-response", value: r })) 
     return state; 

    case "fetch-response": 
     return Object.assign({}, state, { whatever: action.value });; 
    } 
} 
+2

Marcelo、あなたのブログ記事はあなたのアプローチの状況を説明する素晴らしい仕事ですので、私はここにリンクしています:https://lazamar.github.io/dispatching-from-inside-of-reducers/ –

関連する問題