I was having trouble to understand useReducer because I don't have Redux background. So I wrote this article to explain it to myself, and I hope it can help you as well.
Learn useState first
If you know how useState works, then you can skip this section, and I am going to compare useReducer to useState.
useState example (reactjs.org)
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
const [count, setCount] = useState(0);
-
useState(0): It passes the initial state (in this case, it is 0), and returns an array with 2 elements -countandsetCount. -
count: new state -
setCount: the function that uses to change the state value. It is likethis.setState()in class based component.
Compare to useReducer
useReducer is used for complicated state situation.
For example, I want to add one more button to decrease counter.
Here is the code by using useState
useState example (reactjs.org)
const [count, setCount] = useState(0);
return (
<div>
{count}
<button onClick={() => setCount(count + 1)}>
+
</button>
<button onClick={() => setCount(count - 1)}>
-
</button>
</div>
);
}
We are going to move count+1 and count -1 to a FUNCTION by using useReducer
const [count, setCount] = useReducer(FUNCTION, {count: 0})
In useReducer, we call count as state, setCount as dispatch, and FUNCTION is reducer
So it looks like this -
const [state, dispatch] = useReducer(reducer, {count: 0})
MDN explains pretty well what is Array.prototype.reduce() is. It may help you to understand what is the reducer funciton in useReducer.
Next, we are going to write the reducer function
reducer function passes 2 parameters
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
state: the current state
action: if I want to change state, then I call dispatch function. In this case, the first element is type, refer to action.type.
For example, I call dispatch({type: 'increment'}), then count will be + 1.
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
Full codes -
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, {count: 0});
return (
{state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
Hope it will help you! ❤️
Top comments (1)
Wow, useReducer definitely looks cleaner than writing separate handlers for different state updates. Thanks for sharing :)