Overview
-
useStatedoesn't update a value instantaneously, so that value will be as it was even if it's accessed soon afteruseStatelike below.
cf. https://ja.react.dev/reference/react/useState#usage
- React handles the accumulated queues and updates state in bulk after all events executing in parallel completed being done.
- After that, components are re-rendered.
cf. https://ja.react.dev/learn/queueing-a-series-of-state-updates
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log(count); // 0
}
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
useRef
- Use
useRefas "escape hatch" to get the latest updated state whensetStateis called in an asynchronous event(function).
Gist
-
setStateis an asynchronous function. - The queue for updating the state is not captured by the lifecycle of the state in React when it is called in an asynchronous function. So the state is not updated then.
cf. https://crybabe.net/archives/12#toc1
cf. https://ja.react.dev/learn/referencing-values-with-refs#when-to-use-refs
function Example() {
const [count, setCount] = useState(0);
const [count2, setCount2] = useState(0);
const countRef = useRef(0);
const handleClick = () => {
setTimeout(() => {
setCount(count + 1);
console.log(count); // 0
setCount2(count => count + 1);
console.log(count); // 1
countRef.current += 1;
console.log(countRef.current); // 1
}, 1000);
};
const onSubmit = () => {
console.log(count); // 0
console.log(count2); // 0
console.log(countRef.current); // 1
};
return (
<div>
<p>{countRef.current}</p>
<button onClick={handleClick}>Click me</button>
<button onClick={onSubmit}>Submit</button>
</div>
);
}
export default Example;
However, you need to be careful about the point useRef is independent of the React rendering cycle and so doesn't update DOM.
In principle, it's important to design the overall logic in the component so that they can work even if useState runs asynchronously.
Functional update
- Use a functional update way instead of the one to do directly if you wanna access an updated value soon after it's updated by
useStatein the same event(function).
const increaseDouble = () => {
setCount(count + 1); // not done
setCount(count + 1); // done
console.log(count) // 1
};
- First
setCount(count + 1)is called. Butcountis still 0 at this point due to the asynchronous update. - Instantaneously, second
setCount(count + 1)is called. Butcountis still 0 at this point too because 1stsetCountis not done yet.
- So you can use a functional update to resolve that.
cf. https://ja.react.dev/reference/react/useState#updating-state-based-on-the-previous-state
const increaseDouble = () => {
setCount(count => count + 1); // done
setCount(count => count + 1); // done
console.log(count) // 2
};
※ However seems there's no big difference between a functional update and a direct update for the most part except for the case like above.
Top comments (0)