結論
setData を使わずに data を直接書き換えても 「React的な意味での再レンダリングは起きません」。
「data の値が変わったらレンダリングされる」のではなく、「setData が呼ばれたときに再レンダリングがスケジュールされる」 という仕組みです。
なぜ setData を通さないとダメか
useState は内部的に「前回の state 値」を React がどこかに保持しています。
const [data, setData] = useState(initial) で受け取る data は、そのレンダリング時点の 「スナップショット(snapshot)」 でしかありません。
data.xxx = 123 のように直接ミューテート(mutation)しても、React に「state が変わった」という通知は届かないため、再レンダリングは走りません。
❌ ダメな例(直接ミューテーション)
const [data, setData] = useState({ count: 0 });
const bad = () => {
data.count = data.count + 1; // 直接変更
// setData を呼んでいないので、この時点では画面は変わらない
};
この場合、console.log(data.count) は 1 になりますが、コンポーネントは再レンダリングされないので画面表示は更新されません。
✅ 正しい更新パターン(新しい参照を作って setData)
オブジェクトや配列の場合、新しいオブジェクト/配列を作り直して setData に渡す必要があります。
const increment = () => {
setData(prev => ({
...prev,
count: prev.count + 1,
}));
};
setData が呼ばれることで React が「state が変わった」と判断し、再レンダリングがスケジュールされます。
「参照(reference)が変わった」ことがトリガーになるので、ミューテーションではなく新しい値を渡すのがポイントです。
例外的に「変わったように見える」ケース
data がオブジェクトで、そのプロパティを直接書き換えたあとに 他の原因(親の再レンダリングなど) で再レンダリングが起きた場合、たまたまその変更が反映されることがあります。
これは「React が変化を検知して再レンダリングした」のではなく、
「たまたま別要因で再レンダリングされたときに、ミューテート済みのオブジェクトを読んだ」だけです。
まとめ(押さえるべき認識)
✅ 正しい理解
- 「
setDataを呼んだら再レンダリングされる」
❌ 誤解しがち
- 「
dataの中身を変えたら(直接代入しても)レンダリングされる」