0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[React] useRefを理解する!

Last updated at Posted at 2024-05-23

useRefとは?

useRefとはDOM要素の参照を保存したり、レンダリング間で変更されない値を保持するために利用されます。

ユースケース

  • DOM要素の参照を保持
  • レンダリング間で変更されない値を保持

DOM要素の参照を保持する

1. useRefを利用して変数を作成する

  • useRefを実行し、戻り値のRefObjectを変数に取る。
    const inputRef = useRef<HTMLInputElement>(null);
    
    • 引数: 初期値(ここではnull
    • ジェネリック: 保存するReact要素の型(ここではHTMLInputElement

2. refプロパティを使用してDOM要素にRefObjectを指定する

  • input要素にrefプロパティを使用し、RefObjectを指定する
    <input type="text" ref={inputRef} />
    

3. DOM要素にアクセスする

RefObjectに保存した値はcurrentプロパティに保存されています。

const handleClick = () => {
  if (inputRef.current) {
    inputRef.current.focus();
  }
};

return (
  <div>
    <input type="text" ref={inputRef} />
    <button onClick={handleClick}>フォーカス</button>
  </div>
);

:warning: useRefの注意点

currentプロパティに参照が保存されるタイミングは、コンポーネントがマウントされたタイミングになります。

componentDidMount

image.png

引用元

そのため、コンポーネントマウント前にcurrentプロパティにアクセスすると、nullが返されます。

const Sample: React.FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);
  console.log(inputRef.current); // => null

  return (
    <>
      <input type="text" ref={inputRef} />
    </>
  );
};

対処法として、以下2つの方法があります。

  1. useEffectを利用する
  2. refコールバックを利用する

useRef + useEffect

useEffectはコンポーネントのマウント後に実行されるため、currentプロパティに値が格納された後に実行することができます。

const Sample: React.FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);
  console.log(inputRef.current); // => null

  useEffect(() => {
    console.log(inputRef.current); // => <input type="text">
  }, []);

  return (
    <>
      <input type="text" ref={inputRef} />
    </>
  );
};

useRef + refコールバック

ref属性にコールバック関数を渡す方法です。
関数はコンポーネントのマウント時と、アンマウント時に実行されます。
アンマウント時は、引数にnullを与えて関数を呼び出します。

呼び出しは初回のみで良いため、useCallbackを使用して、メモ化しています。

const Sample: React.FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);
  console.log(inputRef.current); // => null

  const inputRefCallback = useCallback((input: HTMLInputElement | null) => {
    if (input === null) return;
    console.log(input); // => <input type="text">
  }, []);

  return (
    <>
      <input type="text" ref={inputRefCallback} />
    </>
  );
};

参考にさせていただいた記事

レンダリング間で変更されない値を保持する

1. useRefを利用して変数を作成する

  • useRefを実行し、変数を作成
    const countRef = useRef(0);
    

2. 変数の値を更新する

  • currentプロパティを使用して、変数の値を更新
    const incrementCount = () => {
      countRef.current += 1;
      console.log(countRef.current);
    };
    

3. 変数の値を使用する

  • 変数の値はコンポーネントが再レンダリングされても保持される
  const MyComponent = () => {
    const countRef = useRef(0);

    const incrementCount = () => {
      countRef.current += 1;
      console.log(countRef.current);
    };

    return (
      <div>
        <p>レンダリング間で変更されない値: {countRef.current}</p>
        <button onClick={incrementCount}>カウントアップ</button>
      </div>
    );
  };

まとめ

  • useRefは、DOM要素の参照を保持するために利用される
  • 上記とは別に、再レンダリングされても値がリセットされない変数を保持するためにも使用される
  • RefObject.currentで値を読み書きし、必要に応じてその値を利用する
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?