Can I dispatch an action in reducer? Can I dispatch an action in reducer? reactjs reactjs

Can I dispatch an action in reducer?


Starting another dispatch before your reducer is finished is an anti-pattern, because the state you received at the beginning of your reducer will not be the current application state anymore when your reducer finishes. But scheduling another dispatch from within a reducer is NOT an anti-pattern. In fact, that is what the Elm language does, and as you know Redux is an attempt to bring the Elm architecture to JavaScript.

Here is a middleware that will add the property asyncDispatch to all of your actions. When your reducer has finished and returned the new application state, asyncDispatch will trigger store.dispatch with whatever action you give to it.

// This middleware will just add the property "async dispatch" to all actionsconst 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;};

Now your reducer can do this:

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 });;  }}


Dispatching an action within a reducer is an anti-pattern. Your reducer should be without side effects, simply digesting the action payload and returning a new state object. Adding listeners and dispatching actions within the reducer can lead to chained actions and other side effects.

Sounds like your initialized AudioElement class and the event listener belong within a component rather than in state. Within the event listener you can dispatch an action, which will update progress in state.

You can either initialize the AudioElement class object in a new React component or just convert that class to a React component.

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.updateProgressAction();  }  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);

Note that the updateProgressAction is automatically wrapped with dispatch so you don't need to call dispatch directly.


You might try using a library like redux-saga. It allows for a very clean way to sequence async functions, fire off actions, use delays and more. It is very powerful!