web-dev-qa-db-ja.com

リデューサーでアクセスを送信できますか?

それ自体が減力剤でアクションをディスパッチすることは可能ですか?プログレスバーとオーディオ要素があります。目標は、オーディオ要素で時間が更新されたときにプログレスバーを更新することです。しかし、私はプログレスバーを更新するために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;
151
klanm

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

あなたの初期化された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を直接呼び出す必要はありません。

119
mtaube

レデューサーが完成する前に別のディスパッチを開始するのはアンチパターンです。レデューサーの最初に受け取った状態は現在のアプリケーション状態ではないためです。もうあなたの減力剤が終わるとき。しかし、レデューサー内から別のディスパッチをスケジュールすることは、アンチパターンではありません。実際、それこそが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 });;
  }
}
106

あなたは redux-saga のようなライブラリを使うことを試みるかもしれません。これは非同期関数のシーケンス、アクションの起動、遅延の使用などを行うための非常にクリーンな方法を可能にします。とても強力です。

10
chandlervdw

redux-loop はElmからヒントを得てこのパターンを提供します。

3
Quang Van