Using useReducer
to handle complicated state is preferable than using useState
. However, writing a reducer is kind of annoying because we may need to write so many switch/case
to determine which action we are going to handle. Furthermore, we may prefer writing action creators to generate actions instead of writing an action directly.
To solve these problems, I write a package called use-case-reducers to simplify our work.
Features of this package
- Use an object to generate a reducer
- Automatically generate action creators
- Allow mutating state in case reducer
Use an object to generate a reducer
Instead of writing a reducer function, use-case-reducers use an object containing case reducers to generate a reducer. A case reducer is a function that only handles one case of actions. For example, if we want to handle a counter state with two actions increment
and add
, then the object may look like:
const caseReducers = {
increment: state => ({count: state + 1}),
add: (state, amount) => ({count: state + amount}),
}
Automatically generate action creators
use-case-reducers will generate all action creators corresponding to the case reducers you pass in. Take the above caseReducers
for example, it will generate two action creators increment()
and add(amount)
.
Allow mutating state in case reducer
This package use immer in the generated reducer, so we can mutate the state inside our case reducers. We can rewrite the above caseReducers
to:
const caseReducers = {
increment: state => {state.count += 1},
add: (state, amount) => {state.count += amount},
}
This feature may be useful when our state is very complicated.
How to use this package
Use npm
or yarn
to install it as a dependency:
npm install use-case-reducers
#or
yarn add use-case-reducers
We are going to write a component with a counter state and use this package to update it.
import useCaseReducers from 'use-case-reducers'
const initialState = {count: 0};
const caseReducers = {
increment: (state) => {state.count += 1},
add: (state, amount) => {state.count += amount},
};
const Counter = () => {
const [state, dispatch, {increment, add}] = useCaseReducers(caseReducers, initialState);
return (
<div>
count: {state.count}
<button onClick={() => dispatch(increment())}>increment</button>
<button onClick={() => dispatch(add(10))}>add 10</button>
</div>
)
}
Let's look the same component but writing with useReducer
.
import {useReducer} from 'react'
const initialState = {count: 0};
const reducer = (state, action) => {
switch(action.type) {
'increment': {
return {count: state.count + 1};
}
'add': {
return {count: state.count + action.payload};
}
}
}
const increment = () => ({type: 'increment'});
const add = (amount) => ({type: 'add', payload: amount});
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
count: {state.count}
<button onClick={() => dispatch(increment())}>increment</button>
<button onClick={() => dispatch(add(10))}>add 10</button>
</div>
)
}
As you can see, we can write less code with use-case-reducers
. Hope this package will help you. Have a good day!
Top comments (5)
This is cool! I'm excited to try it out!
Thank you, and if you encounter any issues while using it, please feel free to let me know.
Hi, I tried it just then. It makes my life much easier by avoiding writing actions and reducer function. However I did encounter an issue. In one of my applications I used Map to store data in the state and this error came back:
[Immer] The plugin for 'MapSet' has not been loaded into Immer. To enable the plugin, import and call
enableMapSet()
when initializing your application.I imported and called
enableMapSet()
in both main.jsx and App.jsx however the error is still there...Do you have any ways to solve this please?I suppose it might be due to the difference in our immer versions. The immer version used in "use-case-reducers" is 9.0.12, so if you install this version, you shouldn't see the error message. However, using Map or Set as state doesn't seem ideal. Have you considered using plain objects instead?
reference
Thank you! I refactored the code and it now works!! Big fan of this package will recommend it to anyone who might be confused about the usage of useReducer. Keep up the good work:)