Reduce redux boilerplate with redux-toolkit
Daniel Tchangang5 min read
You’re looking for an easier way to write your reducer, aren’t you ?
When writing plain redux, you have to define an action
, an actionCreator
and a reducer
to update the store of your app. The reducer is in most cases a big switch to handle all the actions dispatched by actionCreators in your app. This leads to a lot of boilerplate and make the code more difficult to understand. There are many librairies out there aiming to reduce this boilerplate.
At Theodo, we used typesafe-actions to reduce redux boilerplate. The redux team recently recommended @redux-toolkit as the “official recommended approach for writing Redux logic”. Thus, I tested it and wrote this article to show you the advantages of such librairies through a simple example.
The example
The example consist in changing the owner of a car :
- There is one car and 2 possible owners (“JOHN” or “JANE”).
- When you click on one of the owner, the car owner in the redux store is changed to match the owner clicked.
Plain redux (28 lines)
With plain redux as on the code below, there are :
- an
action
(UPDATE_OWNER), - an
actionCreator
(updateOwner) - a
reducer
(itemReducer) to handle all the actions (codesandbox).
The first simplification external librairies provide is to get rid of the action and avoid the switch in the reducer function.
Both typesafe-actions and redux/toolkit give you two functions to simplify action and reducer : createAction and createReducer
- We do not need to keep the UPDATE_OWNER action type anymore in an variable to handle it in the reducer.
- The switch disapears
- Code is easier to read and understand
Typesafe action (21 lines ~ -25%)
- With createAction and createReducer functions there is no action constant UPDATE_OWNER and no verbose switch in our reducer anymore : the reducer reacts directly to the actionCreator updateOwner (codesandbox)
- Type-safe action provides also a clearer way to write the reducer using the chain API (codesandbox)
- You can even combine actions easily and/or chain action handlers:
const counterReducer = createReducer(0)
.handleAction([add, increment], (state, action) =>
state + (action.type === 'ADD' ? action.payload : 1)
)
.handleAction(add, (state=> action) => ...);
The code is clearer and easier to understand
- @reduxjs/toolkit provides also a `createAction` function but used in a slightly different manner see here.
- @reduxjs/toolkit `createReducer` function is used as typesafe-action
@redux/toolkit (18 lines ~ -35%)
- @reduxjs/toolkit goes further and provides a function createSlice, allowing actions and reducers to be created in the same function. It’s very powerfull, give it a try codesandbox.
- Moreover Redux toolkit is shipped with ImmerJS, “a tiny package that allows you to work with immutable state in a more convenient way”.
No need to duplicate the state with spread operators everywhere: You can directly update the state and ImmerJs will take into account the changes and create a new state from the precedent state + the changes you made. Have a look again to the reducer function 👇🏽
const { actions, reducer: itemReducer } = createSlice({
name: "items",
initialState: initialItemState,
reducers: {
updateOwner: (state: ItemState, { payload: { user, item } }: UpdateOwnerAction) => {
// This long code with many ... is replaced by one line thks to ImmerJS
// return {
// ...state,
// [item.id]: { ...state[item.id], owner: user }
// }
state[item.id].owner = user;
}
}
});
This makes reducer and actions easier to understand. Goodbye
"desctructuration ..."
😍.
⚠️ Redux teams recommends to read ImmerJs pitfalls before using it, so read it 😉
- In the end of March 2020, the redux toolkit team added 2 api functions (createEntityAdapter & createAsyncThunk) to simplify reducer logic.
In the last example I use the createEntityAdapter function : the entityAdapter “generates a set of prebuilt reducers and selectors for performing CRUD operations on a normalized state structure.”
After normalizing my itemState and creating the itemsAdapter, I can simplify my reducer function to use the generic entityAdapter.updateOne to handle the ownerUpdate action (check full code here).
This gives you a very fast way to write your reducer logic.
Have a look at redux-toolkit doc to fully understand the createEntityAdapter API
Conclusion :
Reasons to use typesafe-actions
-
If you have a lot of actions to handle and one reducer has to react to several actions then typesafe-actions chaining will be right for you
⚠️ Typesafe-actions maintainer is currently updating their API to simpllify the syntax. If you want to contribute. ⚠️
Reasons to use redux/toolkit
- Easier to write and understand reducers with ImmerJs
- Slicers really concise
- The API encourages you to use a normalized state and provides you with practical CRUD function to update data in your store
As redux/toolkit is the recommended library by the redux team, you should give it a try. Slicers + ImmerJS = 🔥 : your code is shorter and easier to understand. Moreover the createEntityAdapter will help you to normalize your state and write a new reducer faster thanks to the many CRUD functions provided.