createSlice
A function that accepts an initial state, an object full of reducer functions, and a "slice name", and automatically generates action creators and action types that correspond to the reducers and state.
Parameters
createSlice
accepts a single configuration object parameter, with the following options:
function createSlice({ // An object of "case reducers". Key names will be used to generate actions. reducers: Object<string, ReducerFunction | ReducerAndPrepareObject> // The initial state for the reducer initialState: any, // A name, used in action types name: string, // An additional object of "case reducers". Keys should be other action types. extraReducers?: | Object<string, ReducerFunction> | ((builder: ActionReducerMapBuilder<State>) => void)})
reducers
An object containing Redux "case reducer" functions (functions intended to handle a specific action type, equivalent to a single case statement in a switch).
The keys in the object will be used to generate string action type constants, and these will show up in the Redux DevTools Extension when they are dispatched. Also, if any other part of the application happens to dispatch an action with the exact same type string, the corresponding reducer will be run. Therefore, you should give the functions descriptive names.
This object will be passed to createReducer
, so the reducers may safely "mutate" the
state they are given.
Customizing Generated Action Creators
If you need to customize the creation of the payload value of an action creator by means of a prepare callback
, the value of the appropriate field of the reducers
argument object should be an object instead of a function. This object must contain two properties: reducer
and prepare
. The value of the reducer
field should be the case reducer function while the value of the prepare
field should be the prepare callback function.
initialState
The initial state value for this slice of state.
name
A string name for this slice of state. Generated action type constants will use this as a prefix.
extraReducers
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.
Like reducers
, extraReducers
should be an object containing Redux case reducer functions. However, the keys should
be other Redux string action type constants, and createSlice
will not auto-generate action types or action creators
for reducers included in this parameter.
As with reducers
, these reducers will also be passed to createReducer
and may "mutate" their state safely.
If two fields from reducers
and extraReducers
happen to end up with the same action type string,
the function from reducers
will be used to handle that action type.
Action creators that were generated using createAction
may be used directly as the keys here, using
computed property syntax. (If you are using TypeScript, you may have to use actionCreator.type
or actionCreator.toString()
to force the TS compiler to accept the computed property.)
extraReducers
The "builder callback" API for Instead of using a simple object as extraReducers
, you can also use a callback that receives a ActionReducerMapBuilder
instance.
We recommend using this API if stricter type safety is necessary when defining reducer argument objects.
Return Value
createSlice
will return an object that looks like:
{ name : string, reducer : ReducerFunction, actions : Object<string, ActionCreator>,}
Each function defined in the reducers
argument will have a corresponding action creator generated using createAction
and included in the result's actions
field using the same function name.
The generated reducer
function is suitable for passing to the Redux combineReducers
function as a "slice reducer".
You may want to consider destructuring the action creators and exporting them individually, for ease of searching for references in a larger codebase.
Note: the result object is conceptually similar to a "Redux duck" code structure. The actual code structure you use is up to you, but there are a couple caveats to keep in mind:
- Actions are not exclusively limited to a single slice. Any part of the reducer logic can (and should!) respond to any dispatched action.
- At the same time, circular references can cause import problems. If slices A and B are defined in separate files, and each file tries to import the other so it can listen to other actions, unexpected behavior may occur.
Examples
import { createSlice, createAction, PayloadAction } from '@reduxjs/toolkit'import { createStore, combineReducers } from 'redux'
const incrementBy = createAction<number>('incrementBy')
const counter = createSlice({ name: 'counter', initialState: 0 as number, reducers: { increment: state => state + 1, decrement: state => state - 1, multiply: { reducer: (state, action: PayloadAction<number>) => state * action.payload, prepare: (value: number) => ({ payload: value || 2 }) // fallback if the payload is a falsy value } }, // "builder callback API" extraReducers: builder => builder.addCase(incrementBy, (state, action) => { return state + action.payload })})
const user = createSlice({ name: 'user', initialState: { name: '', age: 20 }, reducers: { setUserName: (state, action) => { state.name = action.payload // mutate the state all you want with immer } }, // "map object API" extraReducers: { [counter.actions.increment]: (state, action) => { state.age += 1 } }})
const reducer = combineReducers({ counter: counter.reducer, user: user.reducer})
const store = createStore(reducer)
store.dispatch(counter.actions.increment())// -> { counter: 1, user: {name : '', age: 21} }store.dispatch(counter.actions.increment())// -> { counter: 2, user: {name: '', age: 22} }store.dispatch(counter.actions.multiply(3))// -> { counter: 6, user: {name: '', age: 22} }store.dispatch(counter.actions.multiply())// -> { counter: 12, user: {name: '', age: 22} }console.log(`${counter.actions.decrement}`)// -> "counter/decrement"store.dispatch(user.actions.setUserName('eric'))// -> { counter: 6, user: { name: 'eric', age: 22} }