それ自体が減力剤でアクションをディスパッチすることは可能ですか?プログレスバーとオーディオ要素があります。目標は、オーディオ要素で時間が更新されたときにプログレスバーを更新することです。しかし、私はプログレスバーを更新するために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;
レデューサー内でアクションをディスパッチすることはアンチパターンです。あなたのリデューサーは副作用なしで、アクションペイロードをダイジェストして新しい状態オブジェクトを返すようにすべきです。リデューサー内にリスナーを追加してアクションをディスパッチすると、連鎖アクションやその他の副作用が発生する可能性があります。
あなたの初期化されたAudioElement
クラスとイベントリスナのように聞こえますが、状態ではなくコンポーネント内に属します。イベントリスナ内でアクションをディスパッチすることができます。これはprogress
をstateに更新します。
新しいReactコンポーネントでAudioElement
クラスオブジェクトを初期化するか、単にそのクラスを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
でラップされるので、dispatchを直接呼び出す必要はありません。
レデューサーが完成する前に別のディスパッチを開始するのはアンチパターンです。レデューサーの最初に受け取った状態は現在のアプリケーション状態ではないためです。もうあなたの減力剤が終わるとき。しかし、レデューサー内から別のディスパッチをスケジュールすることは、アンチパターンではありません。実際、それこそが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 });
const res = next(actionWithAsyncDispatch);
syncActivityFinished = true;
flushQueue();
return res;
};
今すぐあなたの減力剤はこれを行うことができます:
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 });;
}
}
あなたは redux-saga のようなライブラリを使うことを試みるかもしれません。これは非同期関数のシーケンス、アクションの起動、遅延の使用などを行うための非常にクリーンな方法を可能にします。とても強力です。
redux-loop はElmからヒントを得てこのパターンを提供します。