Redux Toolkit Dispatch Action in Reducer

·

2 min read

Have you ever encountered a situation where, after dispatching an action to Redux Toolkit, you needed to dispatch another action based on a certain condition or timeout?

I recently created a straightforward Redux slice responsible for managing alert messages in my application. The slice consists of two reducers—one to set the message and another to reset it. An additional feature allows the alert message to be automatically dismissed after a specified time by setting a flag. Here's the code for the slice:

const alertSlice = createSlice({
  name: 'alert',
  initialState: {},
  reducers: {
    setAlert: (state, alert) => ({...state, ...alert.payload}),
    reset: () => ({}),
  },
});

The typical approach would be to dispatch a reset action automatically after a setTimeout following the dispatch of the setAlert action. However, adhering to one of Redux's best practices, reducers must not have side effects. This means that dispatching another action within a reducer function is discouraged. For more details, check out the Redux Style Guide.

To address this, further exploration of the Redux Toolkit documentation revealed the existence of a listener middleware. This middleware allows you to incorporate additional logic based on dispatched actions or state changes. The callback function provided by the middleware has access to both dispatch and getState, making it an ideal solution for this scenario.

Conceptually, you can think of this as being similar to React's useEffect hook, except that it runs logic in response to Redux store updates instead of component props/state updates.

Here's an example of how to use the listener middleware:

const alertListenerMiddleware = createListenerMiddleware()
alertListenerMiddleware.startListening({
  actionCreator: alertSlice.actions.setAlert,
  effect: async (action, listenerApi) => {
    if (action.payload.autoDismiss) {
      setTimeout(() => {
         listenerApi.dispatch(alertSlice.actions.reset())
      }, 5000)
    }
  },
});

To complete the integration of the listener middleware into your Redux store, you need to make a few adjustments. Below is the code snippet to configure your Redux store with the middleware:

const store = configureStore({
  reducer: {
    alert: alertSlice.reducer
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware()
      .prepend(alertListenerMiddleware.middleware),
});

While this approach works seamlessly, there are some key considerations:

  1. Ensure there are proper condition checks in your listener callback before dispatching additional actions.

  2. Exercise caution when selecting which actions to dispatch in the listener callback to avoid unintentional infinite dispatch loops.

In addition to dispatching actions in the listener callback, the listener middleware also provides async workflow functions, allowing you to incorporate more complex asynchronous logic. Refer to the Redux Toolkit documentation for more information.

Curious to see the concepts discussed in action? Explore the live demonstration on CodePen.