コンポーネントが再レンダリングされたとき、useRefの値はリセットされる?
解説
useRefの値は再レンダリングされてもリセットされない。useRefが返すのは{ current: 初期値 }というオブジェクトで、このオブジェクトはコンポーネントのマウントからアンマウントまで同一の参照が維持される。つまり.currentに何を代入しても、再レンダリングをまたいでそのまま残る。「毎回リセットされる」は通常のローカル変数と混同した誤解。関数コンポーネントの中でlet count = 0と書いた場合は再レンダリングのたびに関数が再実行されるのでリセットされるが、useRefはReactが内部で参照を保持しているため別の挙動になる。「初回だけ保持」という動作も存在しない。選択肢Dの「unmountしない限り保持」は事実としては近いが、これはuseStateにも当てはまる性質であり、useRef固有の説明としては不十分。useRefとuseState、値の保持は同じなのに何が違うのかどちらもコンポーネントのライフサイクルを通じて値を保持するが、決定的な違いは値を更新したときに再レンダリングが発生するかどうか。useStateのsetStateを呼ぶと、Reactは再レンダリングをスケジュールし、画面が更新されるuseRefの.currentを書き換えても、Reactは何も検知しない。画面は変わらないこの違いから、「画面に表示する値」はuseState、「画面に表示しないけど保持したい値」はuseRefという使い分けが生まれる。useRefが活躍する実務での典型パターンuseRefの代表的な用途は大きく2つある。1. DOM要素への参照const inputRef = useRef(null); // ボタンを押したらinputにフォーカス const handleClick = () => { inputRef.current.focus(); }; return <input ref={inputRef} />;ref属性にrefオブジェクトを渡すと、Reactがマウント時に.currentへDOM要素を自動で代入してくれる。2. 再レンダリングをまたいで値を覚えておくconst renderCount = useRef(0); useEffect(() => { renderCount.current += 1; console.log(`レンダリング回数: ${renderCount.current}`); });レンダリング回数のカウントや、前回の値の保存(previous value パターン)、setIntervalのIDを保持して後からクリアする、といった場面で使われる。これらはいずれも「画面に出す必要はないが、コンポーネントが生きている間は覚えておきたい」値。ローカル変数ではダメな理由「再レンダリングで画面を更新しないなら、普通の変数でよくない?」という疑問が出るが、関数コンポーネントは再レンダリングのたびに関数本体が再実行される。つまりローカル変数は毎回初期化されてしまう。useRefはReactが内部で値を退避してくれるので、再実行されても前回の値が残る。この仕組みを理解しておくと、「なぜletで書いたカウンターが常に0になるのか」というよくあるバグの原因がすぐわかるようになる。