Redux Toolkit history API push

Viewed 1121

In some react/redux applications a transition to a route after some action has been dispatched must occur. In my case, I have exactly this situation. let me show you one reducer of a state slice.

nextStep: (state, action) => {
  const { steps } = state;

  if (steps[state.currentTopNavStep][state.currentStep + 1]) {
    state.currentStep++;
  } else {
    state.currentTopNavStep++;
    state.currentStep = 0;
  }
  if (action.payload.history) {
    const nextStep = steps[state.currentTopNavStep][state.currentStep];
    const { gendersMatch, valuesExist } = nextStep;
    let realNextStep;

    if (
      (valuesExist && valuesExist(state, fbObject)) ||
      (gendersMatch && gendersMatch(state))
    ) {
      state.currentStep++;
      realNextStep = steps[state.currentTopNavStep][state.currentStep];
    } else {
      realNextStep = nextStep;
    }

    const newRoute = realNextStep.route;
    action.payload.history.push(newRoute.path);
  }
}

On the last line I want to push a new route into the history, which I get as a payload when this action is dispatched. I would like to move the logic inside of the reducer into a middleware. reduxToolkit provides createAsyncThunk function. I want to get something like this:

const nextStep = createAsyncThunk(
  'state/nextStep',
  async (history, thunkAPI) => {
    const { state } = thunkAPI.getState();
    const { steps } = state;

    if (steps[state.currentTopNavStep][state.currentStep + 1]) {
      state.currentStep++;
    } else {
      state.currentTopNavStep++;
      state.currentStep = 0;
    }
    if (history) {
      const nextStep = steps[state.currentTopNavStep][state.currentStep];
      const { gendersMatch, valuesExist } = nextStep;
      let realNextStep;

      if (
        (valuesExist && valuesExist(state, fbObject)) ||
        (gendersMatch && gendersMatch(state))
      ) {
        state.currentStep++;
        realNextStep = steps[state.currentTopNavStep][state.currentStep];
      } else {
        realNextStep = nextStep;
      }

      const newRoute = realNextStep.route;
      const res = await history.push(newRoute.path);
      return res; //this will be the action payload
    }
  }
);  

And then use this inside extraReducers:

 extraReducers: {
    [nextStep.fullfilled]: (state, action) => {
       //Here I face the problem, how to handle it in the reducer
      return action.payload;
    },
  },

Can Anybody help me with this case

1 Answers

The thing is that a middleware must not change state.
You will really have to do these separately from each other:

  • reducer changes state
  • middleware does side effects

Also, createAsyncThunk is not the right tool here. The purpose of cAT is to have a thunk that dispatches a "pending" action and then a "fulfilled" or "rejected" action. You don't seem to have any use for these here at all.
You either need to write a normal thunk by hand that first dispatches an action, after that dispatch gets the new state and then does the side effect, or you're gonna have to write a middleware for that yourself (which is pretty simple, as well, the docs have examples on that).

Related