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 の特徴です。
- Stores data
- Does not cause re-render when the data it stores changes
- 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
という風に扱っていたものと同じな気がするんですが、それもあまりやった覚えがないのでいい具体例が出てこない…。
ただ覚えておいて損はない特性だと思うので、この記事がいつか役に立てると幸いです。