Ryan Pergent
Ryan Pergent

Reputation: 5306

Redux toolkit: Have two slices reference each other's actions in extraReducers?

I would like two different slices to cross-reference each other's actions like so:

const sliceA = createSlice({
    name: "sliceA",
    initialState: null,
    reducers: {
        someReducer: (state, action) => {
            // do something
        },
    },
    extraReducers: {
        [sliceB.actions.anotherReducer]: (state, action) => {
            // do something
        },
    },
});

const sliceB = createSlice({
    name: "sliceB",
    initialState: null,
    reducers: {
        anotherReducer: (state, action) => {
            // do something else
        },
    },
    extraReducers: {
        [sliceA.actions.someReducer]: (state, action) => {
            // do something else
        },
    },
});

The problem is that I receive the error that sliceB is undefined when trying to set the extraReducers for sliceA.

I would like to keep the slices separate for clarity, but that some of their actions affect each other.

What is a good way to achieve that?

Upvotes: 7

Views: 5502

Answers (1)

adamz
adamz

Reputation: 353

You have to resolve the circular dependency between the slices by factoring out the creation for the one of the actions into a createActions call that both createSlice calls can reference as extraReducers without their definition being circular.

also note your action naming, and then the line here, is misleading: [sliceA.actions.someReducer]: (state, action) => {

You createSlice is making both actions and reducers, so use a name for the action that doesn't imply it's a reducer.

use createAction: https://redux-toolkit.js.org/api/createAction

See circular ref notes here: https://redux-toolkit.js.org/usage/usage-guide#exporting-and-using-slices

const actionOnAThatAffectsB = createAction('B_ACTION_NAME', function prepare(text) {
  // this function is an optional parameter to createAction.
  // only use it if you want to add to the eventual payload.
  return {
    payload: {
      text,
      id: nanoid(),
      createdAt: new Date().toISOString()
    }
  }
})

const sliceB = createSlice({
    name: "sliceB",
    initialState: null,
    reducers: {
        actionOnBThatAffectsA: (state, action) => {
            // do main stuff on A
        },
    },
    extraReducers: {
        [sliceA.actions.someAction]: (state, action) => {
            // do other stuff.
        },
    },
});

const sliceA = createSlice({
    name: "sliceA",
    initialState: null,
    reducers: {},
    extraReducers: {
        [sliceB.actions.actionOnBThatAffectsA]: (state, action) => {
            // do extra stuff on A
        },
        [actionOnAThatAffectsB]: (state, action) => {
            // you can't create the *action* here with createSlice's default creation through the reducers{} parameter — since that leads to a circular reference during creation.
            // You *are* creating the *reducer* itself here, though.
        },
    },
});

Upvotes: 6

Related Questions