3
1

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 1 year has passed since last update.

ta1m1kamが一人で書くAdvent Calendar 2022

Day 17

ReactHooksについてまとめる(Ref Hooks)

Last updated at Posted at 2022-12-16

概要

Refは、DOMノードやタイムアウトIDなど、レンダリングに使用されない情報をコンポーネントに保持させることができる。
Stateとは異なり、Refを更新してもコンポーネントは再レンダリングされません。RefはReactのパラダイムからの「逃げ道」です。組み込みのブラウザAPIなど、React以外のシステムで動作させる必要がある場合に便利です。

useRef

useRefは同じオブジェクトを返します。currentプロパティを変更して情報を保存して、あとでそれを読み出すことができます。

refを変更しても、再レンダリングは行われません。 つまり、refはコンポーネントの視覚的な出力に影響を与えない情報を保存するのに最適なのです。例えば、インターバルIDを保存しておき、後でそれを取得する必要がある場合、それをrefに入れることができます。ref内の値を更新するには、そのcurrentプロパティを手動で変更する必要があります。

refを使用することによるメリット

  • 再レンダリングされても情報を保存することができる
  • refを変更しても、再レンダリングのトリガーにはならない(stateとは異なります。)
  • コンポーネント間で共有される。

refを変更しても再レンダリングを行わないということは、画面に表示したい情報を保存するにはrefは適切ではありません。

上のcodesandboxの中で、{ref.current}を画面に表示するように修正しても、ボタンをクリックしてref.currentに加算してるのにも関わらず画面は再レンダリングされないために表示は0の初期値のまま変わりません。

refを利用してDOMを操作する

refオブジェクトを宣言します。useRefの引数には初期値を入れます。

import { useRef } from 'react';

function MyComponent() {
    const inputRef = useRef(null);
    // ...

次に、作成したrefオブジェクトを操作したいDOMのref属性に代入します。

return <input ref={inputRef} />;

refオブジェクトのcurrentプロパティからのDOMノードにアクセスして、forcus()などのDOMのメソッドを呼び出すことができます。

function handleClick() {
    inputRef.current.focus();
}

useRefを使用した例

カスタムコンポーネントに対して、refを使用したい時

const inputRef = useRef(null);

return <MyInput ref={inputRef} />;

↑ だとカスタムコンポーネントにrefsを与えられない旨のWarningが表示されます。

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

そんな時は forwardRefでラップして、その中のDOMにref属性を与えることで親コンポーネントからrefを取得することができるようになります。

import { forwardRef } from 'react';

const MyInput = forwardRef(({ value, onChange }, ref) => {
  return (
    <input
      value={value}
      onChange={onChange}
      ref={ref}
    />
  );
});

export default MyInput;

useImperativeHandle

refで参照できるDOMの関数をカスタマイズすることができます。

例えば、 DOMノード全体を公開するのではなく、その中の2つのメソッド(focusとscrollIntoView)のみを使用したいとします。これを行うには、実際のブラウザDOMを別のrefに保持します。そして、useImperativeHandle を使って親コンポーネントが呼び出したいメソッドのforcus()scrollIntoView()だけを含む関数を定義します。

import { forwardRef, useRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => {
    return {
      focus() {
        inputRef.current.focus();
      },
      scrollIntoView() {
        inputRef.current.scrollIntoView();
      },
    };
  }, []);

  return <input {...props} ref={inputRef} />;
});

useImprerativeHandleの使用例

カスタマイズ

使用するメソッドは、DOM のメソッドと完全に一致する必要はありません。なので、開発者が任意にカスタマイズして処理を作成することも可能です。

以下の例では、ボタンをクリックした際に親コンポーネントがリストをスクロールしてテキストボックスにフォーカスを当てる動きを scrollAndFocusAddComment が行っています。

参考

3
1
0

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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?