- useState
- useState use cases
- State management
- Conditional rendering
- Toggle flags
- Counter
- Get API data and store it in state
- useReducer
- useReducer use cases
- Manage multiple states
- Modify complex states, such as arrays or objects: login form
- useEffect
- useEffect use cases
- Running once on mount: fetch API data
- Running on state change: validating input field
- Running on state change: live filtering
- Running on state change: trigger animation on new array value
- Running on props change: update paragraph list on fetched API data update
- Running on props change: updating fetched API data to get updated BTC price
- useMemo
- useMemo use cases
- Expensive function call: get a list of Reddits and sort them alphabetically
- Expensive function call: create a complex initial value counting how many posts have more than 10 votes in a subreddit
- useCallback
- useCallback use cases
- useRef
- useContext
useState
useState is a Hook that needs to be called inside a function component to add some local state to it. React will preserve this state between component re-renders.
There are many use cases for the useState hook, but in this article, I will focus on the following five:
useState use cases
- State management
- Conditional rendering
- Toggle flags (true/false)
- Counter
- Get API data and store it in state
State management
Let's start with a warning: don't write code in this way, because it will create an infinite loop:
The loop is created because the initial render calls the state update function setState, which in time triggers a re-render and a new function evaluation.
If we want to change a state due to an action performed by the user, we can do this:
That state will be preserved across component re-renders and we will be able to make use of it in the newest re-render.
Conditional rendering
We can use a state to conditionally render a component or part of it.
Toggle flags
useState can be used to toggle between two values, usually true and false, in order to toggle a flag, such as the display mode:
The result will be an alternation between dark and light mode on the component.
Counter
useState can be used to keep track of a variable through multiple re-renders, such as in a counter application:
Get API data and store it in state
A more complex use of this hook is presented when we need to interact with an API. In this case, we can use a state to store the response of a fetch() to the API, and the state of a spinner that will indicate if the data is being fetched.
You can watch all these examples live here: https://6142b64c2e448f0007271a3f--romantic-booth-593b0c.netlify.app/usestate/state-management
You can also take a look at the code in this repository: https://github.com/Colo-Codes/react-hooks-use-cases/tree/main/src/components/UseCaseUseState
useReducer
useReducer is a Hook that allows us to manage multiple states more efficiently, create complex state logic, and manage states that depend on previous states. The following two use cases are good examples of how we can make use of this hook.
useReducer use cases
- Manage multiple states: modify an array
- Modify complex states, such as arrays or objects: login form
Manage multiple states
useReducer can be used to simplify the way in which multiple states impact a piece of data. In this case, adding, removing, and clearing an array can be achieved by using useReducer instead of three separate states.
Modify complex states, such as arrays or objects: login form
useReducer can be especially handy when dealing with multiple states and a complex state logic.
By handling a login form with this hook instead of multiple useState hooks we can appreciate how powerful this hook is.
Helper function for simulating a login API (thanks to Harry Wolff for this code):
export async function loginHelper({ username, password }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (username === 'user' && password === 'password') {
resolve();
} else {
reject();
}
}, 1000);
});
}Main component:
useEffect
Whenever we need to make use of side effects in our application, useEffect is the way to go. This hook doesn't present much complications, except for non primitive data types, due to how JavaScript handles them.
According to the official documentation, effects run after every completed render, but you can choose to fire them only when certain values have changed. This hook uses an array of "dependencies": variables or states that useEffect listen to for changes. When their values change, the main body of the useEffect hook is executed.
The return statement of this hook is used to clean methods that are already running, such as timers. The first time this hook is called, its main body is the one that is going to be evaluated first. All other subsequent times the hook is called, the return statement will be evaluated first, and, after that, the hook's main body. This behaviour is especially useful for cleaning code that is already running before run it again, which enable us to prevent memory leaks.
There is an interesting behaviour with this hook when we use non-primitive JavaScript data types as dependencies (e.g., arrays, objects, functions). With primitive values, such as numbers and strings, we can define a variable from another variable, and they will be the same:
const a = 1
const b = 1
a === b
// Output: trueBut with non primitive values, such as objects, this behaviour is not the same:
{} === {}
// Output: falseSo we need to be very careful when using objects as dependencies, because even though they may look as unaltered data, they may not be so. Instead of using objects, we may want to use their properties as dependencies:
useEffect(() => {
// Some code that uses the properties
}, [myObject.property1, myObject.property2]);Now, let's take a look at some use cases for this hook.
useEffect use cases
- Running once on mount: fetch API data
- Running on state change: validating input field
- Running on state change: live filtering
- Running on state change: trigger animation on new array value
- Running on props change: update paragraph list on fetched API data update
- Running on props change: updating fetched API data to get BTC updated price
Running once on mount: fetch API data
When we want to perform an action once, especially when the app loads or mounts, we can use useEffect to do it. In this case, we are triggering a fetch() GET request when the app is mounted, using an empty array as useEffect dependency.
Running on state change: validating input field
Validating an input while it's receiving characters is another great application for useEffect. Whilst the input is being stored in a state using useState, the validation takes place every time the input changes, giving immediate feedback to the user.
We could add a setTimeout() function to check the input field after some time, to delay the checking on each user keystroke, and we would need to clear that timer by using the clearTimeout() function in the return statement of the useEffect hook. A similar example of this is implemented in the useEffect animation trigger, further ahead.
Running on state change: live filtering
We can use useEffect to filter an array "on the fly" by typing letters into an input element. To do so, we will need to use a state to save the input, and a filter implementation inside the useEffect that will be triggered when the input changes, thanks to useEffect dependencies.
Running on state change: trigger animation on new array value
We can use the useEffect hook to trigger an animation on a shopping cart as a side effect of adding a new product to it. In this case, we'll need a state to handle the cart items, and another state to handle the animation trigger.
As we are using a timer inside the useEffect, It is a good practice to clear it before it gets set again by using the return statement of the useEffect, which gets executed before the main body of the useEffect hook gets evaluated (except for the first render).
Running on props change: update paragraph list on fetched API data update
In this use case, we want to trigger a state update due to an updated fetch() call. We are sending the fetched data to a child component, and whenever that data is changed, the child component re-process it.
Running on props change: updating fetched API data to get updated BTC price
In this example, useEffect is used to fetch new data from an API every 3 seconds. The child component useEffect receives the time as dependency, and every time that dependency changes, a new fetch() is triggered. This way, we can have an updated BTC exchange rate in our app.
Finally, you can take a look at these use cases live here, and you can find the source code here.
useMemo
useMemo is a Hook that implements memoization. But, what is memoization? According to Wikipedia, memoization or memoisation is an optimisation technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
Whit the previous definition in mind, we can think of useMemo as a means to manage optimisations that avoid having to perform expensive (resource intensive) calculations.
useMemo has somewhat a similar intent than useCallback, with the difference that useMemo avoids repeating expensive calculations, whilst useCallback avoids recreating methods (functions) at every render. This question in Stack Overflow has further details on this difference.
useMemo use cases
- Expensive function call: get a list of Reddits and sort them alphabetically whenever the list changes
- Expensive function call: create a complex initial value counting how many posts have more than 500 votes in a subreddit
Expensive function call: get a list of Reddits and sort them alphabetically
In this implementation of useMemo an expensive function call is performed each time the user submits a new subreddit to be alphabetically sorted. We can see how, in spite of multiple renders, the calculation is performed only when the data coming from the API changes.
Expensive function call: create a complex initial value counting how many posts have more than 10 votes in a subreddit
Using an empty dependency array in useMemo will trigger the hook during the first rendering of the app.
useCallback
useCallback ...
useCallback use cases
useRef
useContext
useState with useEffect
useState with useRef