State in React
State is a JavaScript object that stores data which can change over time. This data is used to render the user interface (UI) and update it in response to user interactions or other events like timers or network requests.
useState
Managing state within a single component is straightforward using the useState hook:
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
Immutability
React requires state to be immutable. This means you should not modify the state object directly. If you do, React might not correctly detect changes and update the DOM.
The following example will not work as expected:
import React, { useState } from 'react';
const Counter = () => {
const [state, setState] = useState({ count: 1 });
const increment = () => {
state.count += 1;
setState(state);
};
return (
<div>
<p>Count: {state.count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
To fix it, you need to create a new state object:
const increment = () => {
setState({ ...state, count: state.count + 1 });
};
This requirement aligns well with functional programming principles, making languages like ClojureScript, which enforce immutability, a good fit for React development.
While JavaScript does not have built-in immutable data structures, managing nested state can be cumbersome:
setState({
...oldState,
foo: {
...oldState.foo,
count: oldState.foo.count + 1
}
});
Libraries like Immer address this by allowing you to treat state as mutable within a specific scope, simplifying updates:
oldState.foo.count += 1;
Sharing State Between Components
The useState hook is confined to a single component. To share state between components, you typically pass it down as props. This can become complex with multiple nested components. We will explore more efficient solutions in later sections.