O hirunewani blog

Q. useRefでステートを管理していいのか

Created at

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 を読み書きしない。…