State
Definition
A component needs state when some data associated with it changes over time. For example, a Checkbox component might need isChecked in its state, and a NewsFeed component might want to keep track of fetchedPosts in its state.
The most important difference between state and props is that props are passed from a parent component, but state is managed by the component itself. A component cannot change its props, but it can change its state.
For each particular piece of changing data, there should be just one component that “owns” it in its state. Don’t try to synchronize states of two different components. Instead, lift it up to their closest shared ancestor, and pass it down as props to both of them. Just an object that lives inside a component and stores all of the data that that component and maybe some of its children need.
State is local to the component (encapsulated) and should not be accessed outside the component.
State in Function Components
Using the useState
Hook
main.js
function addMinutes(date, minutes) {
//we multiply minutes by 60000 is to convert minutes to milliseconds
return new Date(date.getTime() + minutes * 60000);
}
function Clock() {
const [time, setTime] = React.useState(new Date());
const handleClick = () => {
setTime(addMinutes(time, 10));
};
return (
<div>
<p>{time.toLocaleTimeString()}</p>
<button onClick={handleClick}>+ 10 Minutes</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<Clock />);
What does calling useState do?
It declares a “state variable”. Our variable is called time
but we could call it anything else, like basketball
. This is a way to “preserve” some values between the function calls — useState
is a new way to use the exact same capabilities that this.state provides in a class. Normally, variables “disappear” when the function exits but state variables are preserved by React.
What do we pass to useState as an argument?
The only argument to the useState()
Hook is the initial state
. Unlike with classes, the state doesn’t have to be an object. We can keep a number or a string if that’s all we need. In our example, we just want a date object to show the time, so we pass the a new date object (now) as initial state for our variable. (If we wanted to store two different values in state, we would call useState() twice.)
What does useState return?
It returns a pair of values: the current state and a function that updates it. This is why we write const [date, setDate] = useState(...). This is similar to this.state.count
and this.setState
in a class, except you get them in a pair.
What is that syntax?
The syntax for useState
is confusing at first because it uses Array destructuring to return a pair. Array destructuring is used because it allows the us to decide what the variable and setter function should be named.
Setting state
Remember not to set state
directly, use the setter function returned by the hook.
main.js
function addMinutes(date, minutes) {
//we multiply minutes by 60000 is to convert minutes to milliseconds
return new Date(date.getTime() + minutes * 60000);
}
function Clock() {
let [time, setTime] = React.useState(new Date());
const handleClick = () => {
//doesn't update the DOM
time = addMinutes(time, 10);
//updates the DOM
// setTime(addMinutes(time, 10));
};
return (
<div>
<p>{time.toLocaleTimeString()}</p>
<button onClick={handleClick}>+ 10 Minutes</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<Clock />);
Setting state
based on prior state
Setting state
based on prior state requires passing a function to the updater function that returns the new value instead of just passing the new value.
So if the new state is computed using the previous state...pass a function to your updater function (setX function). The function will receive the previous value, and return an updated value.
main.js
function addMinutes(date, minutes) {
return new Date(date.getTime() + minutes * 60000);
}
function Clock() {
const [time, setTime] = React.useState(new Date());
const handleClick1 = () => {
setTime(addMinutes(time, 10));
setTime(addMinutes(time, 10));
};
const handleClick2 = () => {
setTime((previousTime) => addMinutes(previousTime, 10));
setTime((previousTime) => addMinutes(previousTime, 10));
};
return (
<div>
<p>{time.toLocaleTimeString()}</p>
<button onClick={handleClick1}>+ 10 Minutes</button>
<button onClick={handleClick2}>+ 10 Minutes</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<Clock />);
This is not an issue until you attempt to read state soon after you have set it (setting state repeatedly is an easy way to the issue). The issue arises because React does state updates asyncronously and can batch them to improve rendering performance.
How to be sure a setState call has completed?
Use a useEffect
hook with a dependency on the the state variable that is changing. We will learn about useEffect
in the next chapter.
FAQs
Using Multiple State Variables
Declaring state variables as a pair of [something, setSomething]
is also handy because it lets us give different names to different state variables if we want to use more than one:
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
...
}
In the above component, we have age
, fruit
, and todos
as local variables, and we can update them individually:
function handleOrangeClick() {
// Similar to this.setState({ fruit: 'orange' })
setFruit('orange');
}
You don’t have to use many state variables. State variables can hold objects and arrays just fine, so you can still group related data together. However, unlike this.setState
in a class, updating a state variable always replaces it instead of merging it.
Should I use one or many state variables?
If you’re coming from classes, you might be tempted to always call useState()
once and put all state into a single object. You can do it if you’d like. Here is an example of a component that follows the mouse movement. We keep its position and size in the local state:
function Box() {
const [state, setState] = useState({
left: 0,
top: 0,
width: 100,
height: 100,
});
// ...
}
Now let’s say we want to write some logic that changes left and top when the user moves their mouse. Note how we have to merge these fields into the previous state object manually:
...
const handleWindowMouseMove(e) {
// Spreading "...state" ensures we don't "lose" width and height
setState(state => ({ ...state, left: e.pageX, top: e.pageY }));
}
...
This is because when we update a state variable, we replace its value. This is different from this.setState
in a class, which merges the updated fields into the object.
The React team recommends to split state into multiple state variables based on which values tend to change together.
For example, we could split our component state into position and size objects, and always replace the position with no need for merging:
function Box() {
const [position, setPosition] = useState({ left: 0, top: 0 });
const [size, setSize] = useState({ width: 100, height: 100 });
const handleWindowMouseMove(e) {
setPosition({ left: e.pageX, top: e.pageY });
}
...
}
Where to use useState
In Classes | With Hooks |
---|---|
this.setState | useState |
useState
don’t work inside classes. But you can use function components with hooks instead of class components andsetState
.
Common State Use Case
const { useState } = React;
function App() {
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
function loadData() {
setLoading(true);
setTimeout(() => {
setLoading(false);
setData([1, 2, 3, 4]);
}, 3000);
}
return (
<>
{loading && <p>Loading...</p>}
<pre>{JSON.stringify(data, null, ' ')}</pre>
<button onClick={loadData}>Load Data</button>
</>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
State in Class Components
In React, you don’t manipulate the DOM directly, instead you simply update data (state) and let React react by updating the UI in all the needed places.
class Clock extends React.Component {
state = {
time: new Date().toLocaleTimeString(),
};
handleClick = () => {
this.setState({ time: new Date().toLocaleTimeString() });
};
render() {
return (
<div>
<p>{this.state.time}</p>
<button onClick={handleClick}>Refresh</button>
</div>
);
}
}
ReactDOM.createRoot(document.getElementById('root')).render(<Clock />);
Using State Correctly
There are three things you should know about setState().
- Do Not Modify State Directly
- State Updates :
- In class components,
setState
keeps previous state you do not change - In function components, the
useState
updater functionset...
does not keep previous state. It is overwritten.
State Updates May Be Asynchronous
React may batch multiple set...() calls into a single update for performance.
Because
state
may be updated asynchronously (after an http request or a user action like clicking a button ), you should not rely on current state values for calculating the next state.
Data Flows Down
Neither parent nor child components can know if a certain component is stateful or stateless, and they shouldn’t care whether it is defined as a function or a class.
This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it.
A component may choose to pass its state down as props to its child components:
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
This also works for user-defined components:
<FormattedDate date={this.state.date} />
The FormattedDate
component would receive the date
in its props and wouldn't know whether it came from the Clock
's state, from the Clock
's props, or was typed by hand:
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
This is commonly called a "top-down" or "unidirectional" data flow. Any state is always owned by some specific component, and any data or UI derived from that state can only affect components "below" them in the tree.
If you imagine a component tree as a waterfall of props, each component's state is like an additional water source that joins it at an arbitrary point but also flows down.