📑
useStateの初期値はいつ実行されるのか
Reactのドキュメントを読んでいて、ちょっと不思議なコードがあったので掘り下げてみた。
import { useState } from "react";
export default function CountLabel({ count }) {
const [prevCount, setPrevCount] = useState(count);
const [trend, setTrend] = useState(null);
console.log("count", count);
console.log("prevCount", prevCount);
if (prevCount !== count) {
setPrevCount(count);
setTrend(count > prevCount ? "increasing" : "decreasing");
}
return (
<>
<h1>{count}</h1>
{trend && <p>The count is {trend}</p>}
</>
);
}
最初のレンダリング時は、count prevCount ともにゼロ。
Incrementalボタンを押すと、count は 1、prevCount はゼロ。その直後に count は1、prevCount も1になる。
const [prevCount, setPrevCount] = useState(count);
このコードは、CountLabel コンポーネントが最初にマウントされるタイミングで実行されるだけ。その後に count が更新されても上記のコードは実行されることはない。
コンポーネントが再レンダリングされるたびに count の新しい値で prevCount を更新するわけではないので注意。
prevCount の値は setPrevCount 関数を呼び出して明示的に更新する必要がある。
Incrementalボタンを押すと、コンポーネントは再レンダリングされるが、useState による prevCount の初期化は再実行されない。そのため、prevCount は初期値のまま変わらない。
そしてif文の中が実行される。レンダリング中にsetStateが実行されると強制的に再レンダリングされるので、次回は count と prevCount が同じ値となっている。最終的にレンダーは1回で済むようになる。
useEffect 内で setState を実行するとレンダーが2回走ることになりよくない。もしそういう機会があったら上記のようなレンダリング中に再レンダーをかける方法も思い出してもいいかもしれない。
Discussion