35

The Redux toolkit docs mention using actions (or rather action types) in multiple reducers

First, Redux action types are not meant to be exclusive to a single slice. Conceptually, each slice reducer "owns" its own piece of the Redux state, but it should be able to listen to any action type and update its state appropriately. For example, many different slices might want to respond to a "user logged out" action by clearing data or resetting back to initial state values. Keep that in mind as you design your state shape and create your slices.

But, “keeping that in mind”, what is the best way to achieve this, given that the toolkit puts the slice name at the start of each action type? And that you export a function from that slice and you call that single function to dispatch the action? What am I missing? Does this have to be done in some way that doesn’t use createSlice?

csbarnes
  • 1,873
  • 2
  • 28
  • 35
Jonathan Tuzman
  • 11,568
  • 18
  • 69
  • 129

2 Answers2

28

It looks like this is what extraReducers is for:

One of the key concepts of Redux is that each slice reducer "owns" its slice of state, and that many slice reducers can independently respond to the same action type. extraReducers allows createSlice to respond to other action types besides the types it has generated.

It is a little strange that the action dispatcher should know which reducer the action belongs. I'm not sure the motivation of having reducers and extraReducers, but you can use extraReducers to allow several slices to respond to the same action.

jmellman
  • 456
  • 3
  • 5
  • 7
    RTK encourages "per-feature" slices. So usually, most actions have a "natural" feature they belong to in their core, while other reducers just "go along with it". Sometimes, an action does not really have a "main feature" and in that case it's fine to just create it with `createAction` and reference it in mutliple slices (or plain reducers). – phry Oct 25 '20 at 22:40
0

I've found that using the extraReducers functionality when creating a slice with createSlice is the best way to do it.

In my case I've implemented this by creating a 'SliceFactory' class for each related feature. I've used it to do exactly what is in the example and reset relevant slices on user logout by listening for a LOGOUT_USER action.

Reference:

extraReducers: https://redux-toolkit.js.org/api/createSlice#extrareducer

Original article I used for the factory: https://robkendal.co.uk/blog/2020-01-27-react-redux-components-apis-and-handler-utilities-part-two

import { createSlice } from '@reduxjs/toolkit';
import { LOGOUT_USER } from '../redux/actions';

class CrudReducerFactory {
  constructor(slice, state = null, initialState = {}) {
    state = state || slice;

    this.initialState = initialState;

    const reducerResult = createSlice({
      name: slice,
      initialState: initialState[state],
      reducers: this._generateReducers(),

      extraReducers: (builder) => {
        builder.addCase(LOGOUT_USER, (state, action) => {
          return { ...this.initialState };
        });
      },
    });

    this.reducer = reducerResult.reducer;
    this.actions = reducerResult.actions;
  }

  _generateReducers = () => {
    return {
      // Create One
      requestCreateOne: (state, action) => {
        state.isLoading = true;
      },
      requestCreateOneSuccess: (state, action) => {
        state.isLoading = false;
        state.one = action.payload;
      },
      requestCreateOneError: (state, action) => {
        state.isLoading = false;
      },
      
      // ...snip...
    };
  };
}

export default CrudReducerFactory;

This is instantiated like so:

const factory = new CrudReducerFactory('users', 'users', { foo: 'bah', one: null, isLoading: false } );
The first argument is the name of the slice, the second is the slice of state and the third is the initial state.

You can then use factory.reducer and factory.actions to use accordingly.

Liam
  • 697
  • 8
  • 16
  • 3
    This code does not seem to have any relevance to the question, especially with stuff like `this._generateReducers` missing, no explanation of what the `state` and `slice` constructor arguments should even do. It does not really answer any question that was posed in any way (as the question was more a "understanding something" question). It's just incomplete code without explanation. – phry Oct 25 '20 at 22:37
  • For brevity I did intentionally not insert the missing code but considering the context of creating slices I mistakenly? assumed it wasn't needed. I've tried to explain my answer a little more fully, I hope that helps. – Liam Oct 26 '20 at 00:46