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:
import { useState } from "react";
const UseCaseStateManagement = props => {
const [state, setState] = useState('initial value');
setState('new value');
console.log(state);
return (
<>
<h2>useState use case</h2>
<h3>State management</h3>
<hr />
<p>{state}</p>
</>
);
};
export default UseCaseStateManagement;
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:
import { useState } from "react";
const UseCaseStateManagement = props => {
const [state, setState] = useState('initial value');
console.log('🔄 This is a re-render');
const clickHandler = () => {
setState('new value');
};
return (
<>
<h2>useState use case</h2>
<h3>State management</h3>
<hr />
<button onClick={clickHandler}>Set state</button>
<p>{state}</p>
</>
);
};
export default UseCaseStateManagement;
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.
import { useState } from "react";
const UseCaseConditionalRender = props => {
const [condition, setCondition] = useState(false);
const clickHandler = () => {
setCondition(true);
};
return (
<>
<hr />
<h2>useState use case</h2>
<h3>Conditional Rendering</h3>
<button onClick={clickHandler}>Set condition</button>
{condition && <p>Hello!</p>}
</>
);
};
export default UseCaseConditionalRender;
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:
import { useState } from 'react';
import classes from './UseCaseToggle.module.css';
const UseCaseToggle = props => {
const [mode, setMode] = useState(false);
// Use setState function form because the new state depends on the previous one
const clickHandler = () => {
setMode(prevState => !prevState);
};
const toggledClass = mode ? classes.light : classes.dark;
return (
<div className={toggledClass}>
<hr />
<h2>useState use case</h2>
<h3>Toggle flags</h3>
<button onClick={clickHandler}>Toggle display mode</button>
</div>
);
};
export default UseCaseToggle;
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:
import { useState } from "react";
const UseCaseCounter = props => {
const [counter, setCounter] = useState(0);
// Use setState function form because the new state depends on the previous one
const clickHandlerDecrease = () => {
// Converting the prevState to number to avoid errors
setCounter(prevState => +prevState - 1);
};
const clickHandlerIncrease = () => {
setCounter(prevState => +prevState + 1);
};
return (
<>
<hr />
<h2>useState use case</h2>
<h3>Counter</h3>
<button onClick={clickHandlerDecrease}>--</button>
<span> {counter} </span>
<button onClick={clickHandlerIncrease}>++</button>
</>
);
};
export default UseCaseCounter;
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.
import { useState } from "react";
const UseCaseApi = props => {
const [starship, setStarship] = useState('');
const [isLoading, setIsLoading] = useState(false);
const clickHandler = async () => {
setIsLoading(true);
const response = await fetch('https://swapi.dev/api/starships/10');
const data = await response.json();
setStarship(JSON.stringify(data, null, "\t"));
setIsLoading(false);
};
let message = '';
if (isLoading) {
message = <p>Getting data... 🚀</p>;
}
return (
<>
<hr />
<h2>useState use case</h2>
<h3>Get API data and store it in state</h3>
<button onClick={clickHandler}>Get Millennium Falcon data</button>
<p>{message}</p>
<pre>{starship}</pre>
</>
);
};
export default UseCaseApi;
You can watch all these examples live here.
You can also take a look at the code in this repository.
Top comments (7)
You can also use them to store an element ref!
Using a ref is handy but as you probably know, changes to
ref.current
will not re-render the component.What if you want it to?
You can use a function ref!
It looks like this:
Then in your JSX:
If you aren't familiar with this usage of a ref, a function ref gets called with the element. When the element is removed from the DOM, it is called again and passed
null
.You can reference the DOM element now via the
popup
property (notpopup.current
as this isn't a "standard" ref.It can be useful!
Wow, nice trick! Thanks!
Thank you, don't stop on this one hook, we wait for articles about other hooks. It is very useful information.
I'm actually working on the use cases of other hooks as well, so stay tuned! 😉
Thanks for a good article!
I like reducer concept.
I did work all almost these use cases. This post helped me overview this things. Thank you so much 💯
Thank you! I'm glad you find it helpful. 😄