Skip to content

Writing state slices with Redux Toolkit

Imogen Hardy edited this page Jun 1, 2022 · 4 revisions

A slice is a section of Redux state pertaining to a particular concern- for example personal details, or product selection. A slice has its own reducers and action creators, and its actions are scoped to the slice.

Redux Toolkit makes it extremely easy to define and scope a state slice, using the createSlice utility.

const slice = createSlice({
  name: 'count',
  initialState: {
      count: 0
  },
  reducers: {
    increment(state, action: PayloadAction<number>) {
      state.count += action.payload
    },
  },
})

// now available:
slice.reducer // reducer to use in store creation
slice.actions.increment(2) // action creator

The slice.actions.increment action creator creates an action with the following shape:

{
  type: 'count/increment',
  payload: 2
}

Redux Toolkit uses Immer internally, which removes the burden of managing state immutability ourselves. The state parameter passed to each reducer function is actually a 'writable draft' copy of the state, allowing us to make mutative changes to it which are then added to the next iteration of the real, immutable state. The state type is inferred from the type of the initial state.

Actions created by slice action creators are always of the PayloadAction type, meaning that any information carried with the action (such as the amount to increment by, or the new value of a form field) is under the payload key on the object. This both simplifies the code and removes the need to define our own action types. The type of the action will be automatically scoped to the name of the slice, meaning that we can have, for example, a setEmail action for payee details and a setEmail action for giftee details that will never conflict.

While a slice 'owns' its section of state, reducers and actions, it may also be desirable to respond to actions that belong to other slices- for example, a change to the delivery country that affects available payment options. This can be achieved by adding an extraReducers field in slice creation:

import { commonSlice } from '../common';

const { resetAll } = commonSlice.actions;

const slice = createSlice({
  name: 'count',
  initialState: {
      count: 0
  },
  reducers: {
    increment(state, action: PayloadAction<number>) {
      state.count += action.payload
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(resetAll, (state, action) => {
        state.count = 0;
      })
  },
})

The builder callback approach used here ensures type safety with no need to write out the types ourselves- by using a reference to the action creator from the other slice, the type of action in the callback passed to addCase will be inferred from the action creator.

You should very rarely, if ever, be writing Redux code outside of a slice; however if necessary Toolkit also provides a createReducer helper as well as a mechanism for defining custom async thunks.

πŸ™‹β€β™€οΈ General Information

🎨 Client-side 101

βš›οΈ React+Redux

πŸ’° Payment methods

πŸŽ› Deployment & Testing

πŸ“Š AB Testing

🚧 Helper Components

πŸ“š Other Reference

1️⃣ Quickstarts

πŸ›€οΈ Tracking

Clone this wiki locally