Q. useRefでステートを管理していいのか
useRefでステートを管理する是非について質問を受けたので、個人的な意見をまとめた。
主張
ダメではないが優先すべきではない。
巷の一部
useStateを乱用するな、レンダリングに使わないならuseRef
setStateは再レンダリングを発生させるから、基本的にuseRefを利用すべき
過激派
stateは再レンダリングを発生させるためだけに使え
const counter = useRef(0);
const [, forceRerender] = useReducer((c: number) => c + 1, 0);
return (
<div>
<p>Count: {counter.current}</p>
<button
onClick={() => {
counter.current++;
forceRerender();
}}
>
Increment
</button>
</div>
);
過激派への意見
例えば、React v18で追加されたuseTransitionと併用すると壊れる。
少なくとも元々想定されていた使い方ではない。
react-reduxなどで同様の記述があるという意見があるが、これは過去のもの。
現在は、useSyncExternalStoreが使われている。
巷の一部への意見
少なくとも優先的に使うべきといった主張は誤りだと思う。
refはescape hatchだと言及されている。
“レンダリングに使わないならuseRef”を使っても問題ない(これは公式でも言及がある)が、まずはuseRefを使おう!useRefを使うべきという主張はおかしい。
公式曰く
https://ja.react.dev/reference/react/useRef
useRef
は、レンダー時には不要な値を参照するための React フックです。
https://ja.react.dev/learn/referencing-values-with-refs#differences-between-refs-and-state
ref の方が state よりも「制限が緩い」と感じるかもしれません。例えば、state セッタ関数を使わずに変更できるわけですから。しかし、ほとんどの場合、state を使用することになります。ref は頻繁には必要としない「避難ハッチ」です**。**
https://ja.react.dev/learn/referencing-values-with-refs#when-to-use-refs
通常、ref を使用するのは、コンポーネントが React の外に「踏み出して」、外部 API(多くの場合はコンポーネントの外観に影響を与えないブラウザ API)と通信する必要がある場合…そのような稀な状況の例…タイムアウト ID の保存…DOM 要素の保存と操作…JSX を計算するために必要ではないその他のオブジェクトの保存…
https://ja.react.dev/learn/referencing-values-with-refs#best-practices-for-refs
ref のベストプラクティス…ref を避難ハッチ (escape hatch) として扱う。ref が有用なのは、外部システムやブラウザ API と連携する場合です。アプリケーションのロジックやデータフローの多くが ref に依存しているような場合は、アプローチを見直すことを検討してください。…レンダー中に
ref.current
を読み書きしない。…