↓サンプルコード
import { useState } from "react";
export default function Counter() {
const [foo, setFoo] = useState(1);
// 書き方①: 関数型アップデート
const handleClickFunc = () => {
setFoo((prevFoo) => prevFoo + 1);
};
// 書き方②: 現在値を直接使用
const handleClickDirect = () => {
setFoo(foo + 1);
};
return (
<div>
<h1>{foo}</h1>
<button onClick={handleClickFunc}>
関数型アップデートで +1
</button>
<button onClick={handleClickDirect}>
現在値で +1
</button>
</div>
);
}
React の useState で状態を更新する際、以下の二つの書き方は似ているようで 挙動に違いがある。
// 書き方①
const handleClick = (e) => {
setFoo((foo) => foo + 1);
};
// 書き方②
const handleClick = (e) => {
setFoo(foo + 1);
};
書き方①: 関数型アップデート
- setFoo に関数 (foo) => foo + 1 を渡している
- 引数 foo には 常に最新の状態が渡される
- 複数回の更新や非同期処理が絡んでも、状態の競合が起きにくい
- 推奨される書き方である
setFoo((foo) => foo + 1);
setFoo((foo) => foo + 1); // 2回呼んでも foo は正しく +2 される
現在の変数を直接使用
- 現在の状態 foo を使って更新している
- ただし、この foo は レンダリング時に関数内で評価された値
- 複数回の連続更新や非同期処理で、最新状態が反映されない可能性がある
- 単純なクリックなど、1 回ずつの更新であれば問題はないが、安全ではない
setFoo(foo + 1);
setFoo(foo + 1); // 連続で呼ぶと +1 しか反映されないことがある
まとめ
- 複数回の更新や非同期処理に対応するには、関数型アップデート (prev) => prev + 1 を使うべきである
- 単純な 1 回クリックの処理であれば foo + 1 でも動作するが、安全性が低い
- React では 状態の更新は常に「最新の状態」を基に行う ことが推奨される