📑
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