20
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お嬢様が教えるuseRef()

Last updated at Posted at 2024-06-05

教材

ShinCodeさんの

完全保存版】React Hooksを完全に理解するHooksマスター講座【React18~19対応】

を元に、私が学んだ内容をまとめていきますわ。

お嬢様的Hooks解説

お嬢様が解説するuseState()

お嬢様が解説するuseEffect()

お嬢様が解説するuseRef()

useRef

const ref = useRef(initialValue)

useRefは、レンダー時に不要な値を参照するためのReactフックですの。useRefを使用することで、値を保持しつつもコンポーネントの再レンダリングを防ぐことができますの。

useRefの特性

  • 値の保持: useRefは値を保持するために使用されますの。
  • 再レンダリングしない: useStateと違い、useRefの値を変更してもコンポーネントは再レンダリングされませんの。

例:useRefの使用方法

以下にuseRefを使用したコード例を示しますわ。

import { useRef } from "react";

const Lesson3_1 = () => {
  const ref = useRef(0);
  console.log(ref);

  function handleClick() {
    ref.current = ref.current + 1;
    alert(ref.current);
  }
  return (
    <div>
      <input type="text" />
      <button onClick={handleClick}>Click me!</button>
      <p></p>
    </div>
  );
};

export default Lesson3_1;

説明

  1. useRefの初期化:
    const ref = useRef(0);で、refという変数に0を初期値として保持しますの。

  2. handleClick関数:
    ボタンがクリックされると、handleClick関数が実行され、ref.currentの値が1増加しますの。

    なんで、refじゃなくて、ref.currentを操作するのかというと、refオブジェクト自体が参照のコンテナであり、実際の参照されている値やDOMノードは ref.current に格納されているからですの。

    スクリーンショット 2024-06-05 23.53.48.png


  3. レンダリング後も値を保持:
    ref.currentはレンダリング後も値を保持し続けますの。例えば、inputタグに入力をしても、ref.currentの値は変更されませんの。

スクリーンショット 2024-06-04 19.39.30.png

スクリーンショット 2024-06-04 19.40.02.png

inputタグで何か入力し再レンダリングしても、

スクリーンショット 2024-06-04 19.40.51.png

値は保持されていることが、コンソールでわかると思いますの。

useRefでDOMを取得して画像スクロールを実装してみよう

useRef を使用して、DOM要素を参照し、その要素に対して操作を行うことができます。今回は、ボタンを押すと対応する画像までスクロールする機能を実装しますわ。

初期コード

以下は、useRefを使ってulタグのDOMを取得する基本的なコードですの。

import { useRef } from "react";

const Lesson3_2 = () => {
  const listRef = useRef(null);

  const scrollToIndex = () => {
    console.log(listRef);
  };

  return (
    <div>
      <nav>
        <button onClick={() => scrollToIndex()}>Cat1</button>
        <button onClick={() => scrollToIndex()}>Cat2</button>
        <button onClick={() => scrollToIndex()}>Cat3</button>
      </nav>
      <div style={{ overflowX: "auto", maxWidth: "700px", margin: "auto" }}>
        <ul
          className="flex items-center justify-between"
          style={{ minWidth: "1300px" }} // コンテナより大きいサイズを指定
          ref = {listRef}
        >
          <li>
            <img src="https://loremflickr.com/315/240" alt="Cat 1" />
          </li>
          <li>
            <img src="https://loremflickr.com/300/240" alt="Cat 2" />
          </li>
          <li>
            <img src="https://loremflickr.com/280/240" alt="Cat 3" />
          </li>
        </ul>
      </div>
    </div>
  );
};

export default Lesson3_2;

実装例:ボタンを押すと画像がスクロール

次に、ボタンを押すと指定した画像までスクロールする機能を実装しますわ。useRefを使ってul要素を参照し、その子要素である画像にスクロールしますの。

import { RefObject, useRef } from "react";

const Lesson3_2 = () => {
  const listRef: RefObject<HTMLUListElement> = useRef<HTMLUListElement>(null);

  const scrollToIndex = (index: number) => {
    console.log(listRef);
    const listNode = listRef.current;
    const imgNode = listNode?.querySelectorAll("li > img")[index];
    console.log(imgNode);

    imgNode?.scrollIntoView({
      behavior: "smooth",
      block: "nearest",
      inline: "center"
    })
  };

  return (
    <div>
      <nav>
        <button onClick={() => scrollToIndex(0)}>Cat1</button>
        <button onClick={() => scrollToIndex(1)}>Cat2</button>
        <button onClick={() => scrollToIndex(2)}>Cat3</button>
      </nav>
      <div style={{ overflowX: "auto", maxWidth: "700px", margin: "auto" }}>
        <ul
          className="flex items-center justify-between"
          style={{ minWidth: "1300px" }} // コンテナより大きいサイズを指定
          ref = {listRef}
        >
          <li>
            <img src="https://loremflickr.com/315/240" alt="Cat 1" />
          </li>
          <li>
            <img src="https://loremflickr.com/300/240" alt="Cat 2" />
          </li>
          <li>
            <img src="https://loremflickr.com/280/240" alt="Cat 3" />
          </li>
        </ul>
      </div>
    </div>
  );
};

export default Lesson3_2;

スクリーンショット 2024-06-05 2.32.10.png

説明

  1. useRefの初期化:
    const listRef: RefObject<HTMLUListElement> = useRef<HTMLUListElement>(null); で、ul要素を取得しますの。

  2. scrollToIndex関数:
    scrollToIndex関数は、リストの特定の画像までスクロールしますの。listRef.currentからul要素を取得し、その子要素である画像をquerySelectorAllで取得しますの。

    取得した画像要素に対してscrollIntoViewメソッドを使用し、スムーズにスクロールしますの。

  3. DOM要素へのアクセス:
    ref属性を使用してulタグをlistRefに割り当てますの。これにより、scrollToIndex関数内でlistRef.currentを介してul要素にアクセスできますの。

UseRef()を使って余計な再レンダリングを防ぐ方法

useRefを使用することで、コンポーネントの不要な再レンダリングを防ぐことが出来ますの。これにより、パフォーマンスを向上させることができますわ。

状態管理による再レンダリング

以下のコードでは、useState を使用して、inputを管理している為、onChange イベントが発生されるたびに再レンダリングされますわ。

import { useState } from "react";

const Lesson3_3 = () => {

  const [inputText, setInputText] = useState("");

  console.log("render")

  for(let i; i < 50; i++) {
    console.log("aiueo");
  }

  const handleClick = () => {
    alert(inputText);
  };

  return (
    <div>
      <input type="text" className="border-b" value={inputText} onChange={(e) => setInputText(e.target.value)}/>
      <button onClick={handleClick}>input入力値を見る</button>
    </div>
  );
};

export default Lesson3_3;

useRefを使用して再レンダリングを防ぐ

以下のコードでは、useRefを使用してinputの値を管理し、再レンダリングを防いでいますの。

import { useRef, useState } from "react";

const Lesson3_3 = () => {

  // const [inputText, setInputText] = useState("");
  const inputRef = useRef<HTMLInputElement>(null);

  console.log("render")

  const handleClick = () => {
    alert(inputRef.current?.value)
  };

  return (
    <div>
      <input type="text" className="border-b" ref={inputRef} />
      <button onClick={handleClick}>input入力値を見る</button>
    </div>
  );
};

export default Lesson3_3;

説明

  1. useRefの初期化:
    const inputRef = useRef<HTMLInputElement>(null);inputの値を参照しますの。

  2. 再レンダリングの防止:
    useStateを使用せずに、useRefを使うことで、inputの値の変更による再レンダリングを防ぎますの。

    onChangeイベントが発生しても、コンポーネントは再レンダリングされませんの。

  3. inputの値の取得:
    inputRef.current?.valueを使用してinputの値を取得しますの。

    handleClick関数内で、ボタンがクリックされたときにinputの値をアラートで表示しますの。

実際の使用例

useRefは、以下のような場合に非常に便利ですわ。

  • パフォーマンスの最適化:
    • 重い処理が含まれる場合、再レンダリングを防ぐことでパフォーマンスを最適化しますの。
  • DOM要素への直接アクセス:
    • input要素の値を直接参照する場合など、DOM要素に対して直接操作を行いたい場合に使用しますの。
  • ECサイトでの検索機能:
    • 商品検索の入力フィールドなど、頻繁に更新されるが再レンダリングを避けたい場合に有効ですわ。

forwardRef()で別のコンポーネントのDOMノードにアクセスする

forwardRefを使用すると、親コンポーネントから子コンポーネントにrefを渡すことができ、子コンポーネントのDOMノードに直接アクセスできますの。以下に、その実装例を示しますわ。

親コンポーネントの実装

親コンポーネントであるLesson3_4から子コンポーネントMyVideoPlayerrefを渡し、動画の再生と一時停止を制御しますの。

import { useRef } from "react";
import { MyVideoPlayer } from "./MyVideoPlayer";

const Lesson3_4 = () => {
  const videoRef = useRef<HTMLVideoElement>(null);

  return (
    <div>
      <button onClick={() => videoRef.current?.play()}>Play</button>
      <button onClick={() => videoRef.current?.pause()}>Pause</button>
      <br />
      <MyVideoPlayer
        ref={videoRef}
        src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
        type="video/mp4"
        width="250"
      />
    </div>
  );
};

export default Lesson3_4;

子コンポーネントの実装

子コンポーネントMyVideoPlayerでは、forwardRefを使用して親コンポーネントから渡されたrefを受け取りますの。

import { forwardRef } from "react";

type MyVideoPlayerProps = {
  width: string;
  type: string;
  src: string;
};

export const MyVideoPlayer = forwardRef<HTMLVideoElement, MyVideoPlayerProps>(
  (props, ref) => {
    return (
      <video width={props.width} ref={ref}>
        <source src={props.src} type={props.type} />
      </video>
    );
  }
);

スクリーンショット 2024-06-05 2.33.46.png

説明

  1. 親コンポーネントの設定:
    useRefを使用してvideoRefを作成し、MyVideoPlayerコンポーネントにrefとして渡しますの。

    ボタンのクリックイベントでvideoRef.current?.play()およびvideoRef.current?.pause()を呼び出して、動画の再生と一時停止を制御しますの。

  2. 子コンポーネントの設定:
    forwardRefを使用して、子コンポーネントMyVideoPlayerが親コンポーネントから渡されたrefを受け取りますの。

    ref<video>要素に渡すことで、親コンポーネントから直接<video>要素にアクセスできるようにしますの。

まとめ

useRefの基本特性:

useRefはレンダリング時に不要な値を保持するためのReactフックですの。useStateとは異なり、値の変更がコンポーネントの再レンダリングを引き起こさないため、パフォーマンスの最適化に有効ですの。

DOM操作の実装例:

useRefを使ってDOM要素にアクセスし、その要素に対する操作を行うことができますの。具体例として、ボタンを押すと対応する画像までスムーズにスクロールする機能の実装がありますの。

再レンダリング防止:

useStateを使う場合と比べて、inputの値の変更による再レンダリングを防ぎ、パフォーマンスを向上させることができますの。

実際の使用例:

useRefはパフォーマンスの最適化やDOM要素への直接アクセスが必要な場合に特に有用ですわ。例えば、ECサイトの検索機能など、頻繁に更新されるが再レンダリングを避けたい場面で効果を発揮しますの。

forwardRefの活用:
forwardRefを使用することで、親コンポーネントから子コンポーネントにrefを渡し、子コンポーネントのDOMノードに直接アクセスすることが可能ですの。これにより、例えば動画の再生や一時停止を親コンポーネントから制御することができますわ。

20
10
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
20
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?