231
125

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

useRef は何をやっているのか

Last updated at Posted at 2019-06-13

useRef は元々あった createRef の hooks 版です。その名の通り、DOMに対する参照を持つために使われるのが主な目的です。

ですがそれ以外の用途にも利用することができます。

最初に私が知ったのはこちらのReact本体のドキュメントにも記されている「以前のstateの値を参照する方法」です
https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state

function usePrevious(value: any) {
  const ref = useRef(null);
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

なにやら useRef で前回の値を保持しているようですね。それまで ref と言えばDOMに対して使うものと思っていたので「え、useRefってDOM以外の値を参照するのに使えるの?」と驚きました。驚きましたが当時の私はとりあえずスルーしました。

次に出会ったのがこちらの記事のこの節。

特に気になったのが次の useRef の特徴です。

  1. Stores data
  1. Does not cause re-render when the data it stores changes
  2. Remembers its stored data even after state change in useState causes a re-render.

気になったものの、この記事を読んだ当時の私は useRef の詳細については華麗にスルーしました。

ですが日々開発していて、たま〜〜〜に useRef をDOM以外に使ったテクを見かけるので、今回深ぼろうと思った次第です。

なのでこの記事では次のようなことを書きます。

  • useRef は具体的になにをしているのか
  • どんなユースケースが考えられるか

useRef は具体的になにをしているのか

実際の実装を見てみましょう。

まずは初期化の処理から。

function mountRef<T>(initialValue: T): {|current: T|} {
  const hook = mountWorkInProgressHook();
  const ref = {current: initialValue};
  if (__DEV__) {
    Object.seal(ref);
  }
  hook.memoizedState = ref;
  return ref;
}

単純に useRef() の引数に入ってきた値を代入しているだけですね。なにも難しいことはないです。次に初期化以降の再描画の時を見てみましょう。

function updateRef<T>(initialValue: T): {|current: T|} {
  const hook = updateWorkInProgressHook();
  return hook.memoizedState;
}

これだけです。少な過ぎてこの記事が企画倒れになる気もしましたが、強い気持ちで書き進めます。このコードを読む限り、 useRefは再描画が行われても単純にメモ化された値を返すだけです。

明示的に値を変えたい場合は

const hoge = useRef(null)

const handleSomething = () => {
  hoge.current = "new value"
}

のように明示的に current というプロパティに代入するしかありません。

ここから直感的に冒頭にあった「Does not cause re-render when the data it stores changes」がなんとなく腹落ちした方もいらっしゃるかもしれません。どういうことかと言いますと useRef で保持した値を変える時はただの値の代入でしかないため state を変えるわけではありません。このため再描画が発生しないのです。そしてグローバルな hooks の領域に値が保存されているため、再計算が行われても値は失われません。

どんなユースケースが考えられるか

useRef で値を保持することの特性は 「再描画を発生させないこと」 これに尽きます。

なのでユースケースとしては

  • 描画には関係のない状態

を扱うために使うのが主となるでしょう。

usePrevious の例は分かりやすいですね。前回の値を保持しておいて、それをコンポーネント内の関数やらの分岐に使うわけです。ただ正直これ以外は私はあまりユースケース思いつかないです。多分ノリ的にはClassコンポーネント時代に、Classのプロパティとして this.xxx という風に扱っていたものと同じな気がするんですが、それもあまりやった覚えがないのでいい具体例が出てこない…。

ただ覚えておいて損はない特性だと思うので、この記事がいつか役に立てると幸いです。

231
125
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
231
125

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?