Redux Toolkit: Dispatch an action from an extraReducers listener?

Viewed 16977

I want to know if it's possible (or a good practice) to call dispatch(someDumbAction()) from an extraReducer.

For example, I have a setData() action in reducers object from createSlice. I want to call setData() directly in my component. But I want to call it too in a extraReducer listener, in order to reuse the reducer logic, like below:

// Thunk Action
export const getData = createAsyncThunk('data/getData', async (params) => {
  return await api.get({ params })
})

// Slice creation
const slice = createSlice({
  name: 'data',
  initialState: [],
  reducers: {
    setData: (state, { payload }) => {
       state.push(payload);
    })
  },
  extraReducers: (builder: any) => {
    builder.addCase(getData.pending, (state) => {
      //...
    })
    builder.addCase(getData.rejected, (state) => {
      //...
    })
    builder.addCase(getData.fulfilled, (state, { payload }) => {
      // Here I want to dispatch `setData` action, in order to reuse that logic
      // dispatch(setData(payload));
      
    })
  },
})

// In any component: dispatch(setData([...]);
1 Answers

No. Reducers can never dispatch actions:

https://redux.js.org/style-guide/style-guide#reducers-must-not-have-side-effects

However, it looks like what you're really asking for here is the ability to run the same state update logic steps in multiple situations.

You could define the logic as a standalone reducer function, and reuse it in both cases:

function addItem(state, action) {
  state.push(action.payload);
}

const slice = createSlice({
  name: 'data',
  initialState: [],
  reducers: {
    setData: addItem
  },
  extraReducers: (builder: any) => {
    builder.addCase(getData.pending, (state) => {
      //...
    })
    builder.addCase(getData.rejected, (state) => {
      //...
    })
    builder.addCase(getData.fulfilled, addItem)
  },
})

You could also define the function as part of reducers, and then reference it inside of the extraReducers handler:

const slice = createSlice({
  name: 'data',
  initialState: [],
  reducers: {
    setData: (state, { payload }) => {
       state.push(payload);
    })
  },
  extraReducers: (builder: any) => {
    builder.addCase(getData.pending, (state) => {
      //...
    })
    builder.addCase(getData.rejected, (state) => {
      //...
    })
    builder.addCase(getData.fulfilled, (state, action) => {
      slice.caseReducers.setData(state, action);
    })
  },
})
Related