Hi there, in my last post we covered useCallback
in react, and now, we'll be looking at one last hook provided to us by the amazing React team: useReducer
What is the useReducer hook?
Before we continue to explain what the hook is all about let's take a step back and look at what a reducer is.
What is a reducer
If you are familiar with redux then you'd know what a reducer function is.
A reducer accepts a state and an action as an argument and returns a new state as a result. Here's a common example of a reducer
const initialState = {
loading: false,
error: false,
names: [],
};
const reducer = (state = initialState, action) => {
switch(action.type) {
case "loading":
return { ...state, loading: true };
case "error":
return { ...state, error: true, loading: false };
case "success":
return { ...state, names: action.payload };
default:
return state;
}
}
What is going on here?
The function above is checking for a type and returning a state based on the type that was passed.
Back to useReducer
The useReducer hook is very similar to the useState hook, it allows you to manage a state and rerender the component whenever your state changes, It accepts a reducer and an initial state (like the example above) and returns a new version of the state and a dispatch method based on the action performed in the reducer.
Here is an example of how it is being used:
const [state, dispatch] = useReducer(reducer, initialState);
The hook also takes a third argument that will lazily initialize the state or reset the state back to its initial state. You can read more on the lazy initialization in react documentation
const [state, dispatch] = useReducer(reducer, initialState, initFunc);
Why should I use this?
The useReducer hook is often used when you have a complex state, or where the initial state is dependent on another state.
To understand this better, we need some actions.
Examples
Let us look at a counter using useState
const [num1, setNum1] = useState(0);
const decrement = () => {
setNum1((prev) => prev - 1);
};
const increment = () => {
setNum1((prev) => prev + 1);
};
return (
<div className="App">
Num: {num1}
<div className="grid">
<button className="box" onClick={increment}>
+
</button>
{num1 ? (
<button className="box" onClick={decrement}>
-
</button>
): (<div />)}
</div>
</div>
);
Now let's look at the same example using useReducer
import { useReducer } from "react";
const initialState = { num: 0 };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { num: state.num + 1 };
case "decrement":
return { num: state.num - 1 };
default:
throw new Error();
}
}
export default function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div className="App">
<div>Num: {state.num}</div>
<div className="grid">
<button className="box" onClick={() => dispatch({ type: "increment" })}>
+
</button>
{state.num ? (
<button className="box" onClick={decrement}>
-
</button>
): (<div />)}
</div>
</div>
);
}
In our reducer function, we're using the type to determine what action will be performed on the state.
This method is better if we're creating a calculator for example.
Conclusion
There's been a lot of arguments on whether to use the useState hook or useReducer, it actually depends on what you're doing but Kent C. Dodds gave very detailed examples on when to use either in his post.
Thank you so much for reading, In my next post we'll be looking at how to create our very own react hook.
If you have any questions or comments please drop them in the comment below. Don't forget to stay safe and keep being amazing.
Top comments (2)
Hi! In the last example you miss to change the onClick for decrement. Thank you for those articles, helped me alot!
Thanks you for pointing that out.
Thanks for reading, I'm really glad it helped you