useState or useReducer – which to choose for your application
adams mercy
Posted on April 4, 2023
useReducer
helps us manage state in a complex application. What exactly is useReducer
? Is it an alternative to useState
? useState
and useReducer
are both used to handle state logic. It is thereby necessary to understand when to use these hooks. useReducer
is not replacing useState
in any way but it would be more efficient to use useReducer
in some complex applications.
We are going to explore these hooks in detail showing the best hook to use in any given application and how we can convert one of these hooks to use the other. Let’s get started.
The commonly used state hook – useState
, is a react hook that lets you add a state variable to your component. It is usually written like this:
const [state, setState] = useState(initialState)
useState
takes in an initialState which can be a string, boolean, number, array, object or function. It returns two values namely:
- state – This is the current state
- setState – This is a function that updates the state.
A simple example looks like this:
const [country, setCountry] = useState('Nigeria');
const handleClick = () => {
setCountry('South Africa');
}
The above code snippet is a simple example of the useState
hook and how we can use it to update the state. If the handleClick
function is passed to a button, on clicking the button, the application re-renders to show the update – “South Africa” for the country state variable.
What exactly is useReducer?
useReducer
is a React hook that lets you add a reducer to your component. So, what is a reducer? A reducer is a function that allows us to specify all the state update logic in a single function.
useReducer
takes in two major parameters and an optional third parameter. It returns two values – state and dispatch. The state is the current state and the dispatch is a function that lets you update the state.
const [state, dispatch] = useReducer(reducer, initialState, initFunc?)
Same simple example with useReducer
:
const reducer = (state, action) => {
switch (action.type) {
case 'change_country': {
return {
country: action.newCountry
}
}
// other cases are written here
default: {
return state
}
}
}
const [state, dispatch] = useReducer(reducer, {country: 'Nigeria'});
const handleClick = () => {
dispatch({
type: 'change_country',
newCountry: 'South Africa'
})
}
An explanation of the above sample code snippet – we invoked our useReducer
which accepts a reducer that contains the logic for updating the state and an initial state of { country: ‘Nigeria’ }
. We are calling the dispatch function in the click handler; passing in the type of action we want to perform with the value of the next state.
Dispatch and Reducer Explained
Dispatch Function: The dispatch function returned by useReducer
lets you update the state to a different value and trigger a re-render. You need to pass the action as the only argument to the dispatch function. An action is usually an object with a type property identifying it and, optionally, other properties with additional information.
dispatch({
type: ‘change_country’,
// other properties
})
A reducer function is declared like the below code snippet. It accepts state, which is the current state and action which updates the state and returns the next state.
function reducer(state, action) {
// state updates are made here
}
We have successfully updated a state using both useState
and useReducer
. So, of what use is one hook over the other? We will cover this next in this article.
Comparing useState and useReducer
useReducer
is similar touseState
.useReducer
enables us to move our state update logic into a single function which is more efficient for larger applications.useState
is simple and easy to set up for smaller applications but when the application gets complex, it becomes difficult to read with many state logic in event handlers.useReducer
proves to be efficient for this use case as it helps organize our state logic in one place.useReducer
is easier to debug in a complex application as you can easily find the action that is not dispatched, but withuseState
, you would have to look through a lengthy code to identify the error.Examining the above code sample, using
useState
resulted in fewer lines of code. While this is good for smaller applications when the application gets larger, the lines of code also increase. UsinguseReducer
to separate the logic in a function is a better approach for larger applications.
If your application is handling multiple state logic in event handlers, then you should use useReducer
else useState
is fine.
How to migrate from useState to useReducer.
We are going to explore an example that uses both useState
and useReducer
. You will, therefore, see how we can migrate from one to the other. Here is a simple application that gets the user’s first_name
and last_name
with a button to add the names to the existing name list and a button to reset the name list.
Steps to follow:
- Move from setting state to dispatching actions.
- Write a reducer function.
- Use the reducer from your component.
This contains the code sample of our application. To see the implementation of our state update logic using useState
, comment out the FormUsingReducer component in App.js and uncomment the Form component in App.js and vice-versa for useReducer
.
Conclusion
We have explored both useState
and useReducer
to update state. We also looked at a real live example, when it is preferable to use these hooks and lastly a quick comparison of both. A lot of references are from the React documentation. Check it out here.
Understanding the concepts we use and exploring new concepts is critical for growth as developers. I hope you have learned something and you are excited about applying what you have learnt in your next project.
Thanks for reading.
Posted on April 4, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.