ReactのuseRefで値をコンポーネント間で共有する実践方法

Reactでは、データの共有や状態管理を効率化するために様々なフックが提供されています。その中でもuseRefは、状態管理のためのuseStateとは異なり、レンダーサイクルの影響を受けない値を保持するために使われます。さらに、useRefはDOM要素の参照だけでなく、値を保持して複数のコンポーネント間で共有する用途にも利用可能です。本記事では、useRefを用いて値をコンポーネント間で効率的に共有する方法について、基本から実践的な活用法までを詳しく解説します。Reactプロジェクトの柔軟性を高める技術を習得しましょう。

目次
  1. useRefの基本的な理解
    1. useRefの仕組み
    2. 主な特徴
    3. 主な用途
  2. useRefを使用するメリット
    1. 1. 再レンダーを回避
    2. 2. 値の永続性
    3. 3. 状態管理の簡略化
    4. 4. DOM要素へのアクセス
    5. 5. カスタムフックでの応用
  3. useRefで値を保持する仕組み
    1. useRefの基本構造
    2. useRefとレンダリング
    3. useRefでの値の保持例
    4. useRefでの複数値の保持
    5. useRefで値を保持する仕組みのまとめ
  4. useRefを使ったコンポーネント間の値の共有
    1. 1. 親コンポーネントを介した値の共有
    2. 2. フォワーディングリファレンスを使った値の共有
    3. 3. 実践例: フォーム状態の同期
    4. useRefで値を共有するメリット
  5. React Contextとの比較
    1. 1. useRefとContext APIの違い
    2. 2. useRefを選ぶべき場合
    3. 3. Context APIを選ぶべき場合
    4. 4. 組み合わせて使うケース
    5. 5. 使い分けのまとめ
  6. よくあるトラブルとその対処法
    1. 1. 値が即座に反映されない
    2. 2. `useRef`の初期値が意図しない動作を引き起こす
    3. 3. DOM要素が正しく参照できない
    4. 4. 不必要に`useRef`を使用している
    5. 5. 不要な再レンダーの発生
    6. まとめ
  7. 応用例: フォームの状態管理
    1. 1. シンプルなフォームデータ管理
    2. 2. 入力フィールドへのフォーカス制御
    3. 3. 動的なフォームフィールドの管理
    4. 4. バリデーションの実装
    5. まとめ
  8. 演習問題: 実践的な練習
    1. 演習1: クリックカウントの管理
    2. 演習2: フォームデータの管理
    3. 演習3: 動的フォームの作成
    4. 模範解答
  9. まとめ

useRefの基本的な理解

ReactのuseRefは、特定の値やDOM要素への参照を保持するためのフックです。通常、Reactのコンポーネントは再レンダー時に変数や状態がリセットされますが、useRefを利用すると、再レンダーによって影響を受けない永続的な値を管理できます。

useRefの仕組み

useRefは、次のように利用します。

import { useRef } from "react";

function MyComponent() {
  const ref = useRef(0); // 初期値を0に設定

  const increment = () => {
    ref.current += 1; // currentプロパティを使って値を更新
    console.log(ref.current);
  };

  return <button onClick={increment}>Increment</button>;
}

ここで、ref.currentuseRefによって作成された永続的なプロパティであり、値の更新や参照に使用されます。

主な特徴

  1. 値の永続性
    useRefで管理される値は、再レンダー間で保持されます。
  2. 再レンダーのトリガーにならない
    useRefの値を変更してもコンポーネントは再レンダーされません。
  3. 初期値の設定
    useRefは初期値を設定でき、nullや任意の値を指定可能です。

主な用途

  • DOM要素への参照(例: フォーカスの制御やスクロール位置の取得)
  • 状態管理に依存しない値の保持
  • 再レンダーを避けるための一時的なデータストア

useRefはシンプルながら強力な機能を持つフックであり、React開発において重要な役割を果たします。

useRefを使用するメリット

React開発において、useRefは他の状態管理フック(例: useState)にはないユニークな特性を持っています。そのため、特定のシナリオでuseRefを使用することには多くの利点があります。

1. 再レンダーを回避

useRefを使って値を管理する場合、その値が変更されてもコンポーネントは再レンダーされません。この特性により、パフォーマンスを向上させることができます。

例: カウントの状態を追跡しつつ、再レンダーを最小化

import { useRef } from "react";

function Counter() {
  const count = useRef(0);

  const increment = () => {
    count.current += 1; // 値は更新されるが、レンダーされない
    console.log(count.current); // 最新値をコンソールに出力
  };

  return <button onClick={increment}>Increment</button>;
}

2. 値の永続性

useRefで保持された値は、コンポーネントのライフサイクル全体にわたって永続的に存在します。これにより、再レンダー間で情報が失われることがありません。

例: 前回のレンダーからのデータ比較

import { useRef, useEffect } from "react";

function Example({ value }) {
  const previousValue = useRef(value);

  useEffect(() => {
    console.log("前回の値:", previousValue.current);
    previousValue.current = value; // 現在の値を保存
  }, [value]);

  return <div>現在の値: {value}</div>;
}

3. 状態管理の簡略化

useRefは再レンダーを伴わないため、シンプルなデータ管理が可能です。これにより、複雑な状態管理を回避でき、コードが読みやすくなります。

4. DOM要素へのアクセス

useRefを使うことで、DOM要素に直接アクセスして操作できます。これにより、特定の操作(例: フォーカス設定やスクロール位置の取得)が簡単になります。

例: フォーカスをプログラムで設定

import { useRef } from "react";

function FocusInput() {
  const inputRef = useRef(null);

  const setFocus = () => {
    inputRef.current.focus(); // DOM要素への直接操作
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={setFocus}>Focus Input</button>
    </div>
  );
}

5. カスタムフックでの応用

useRefを用いることで、特定の状態や値を保持するカスタムフックを作成できます。これにより、複数のコンポーネントで共通のロジックを再利用できます。

useRefの活用は、状態管理を効率化し、React開発をシンプルかつ直感的にします。正しい場面で適切に利用することで、コードのパフォーマンスと可読性を大幅に向上させることが可能です。

useRefで値を保持する仕組み

useRefを用いると、Reactコンポーネント内で値を永続的に保持できます。その仕組みを理解することで、useRefの効果的な活用方法を学べます。

useRefの基本構造

useRefを利用すると、特殊なオブジェクトが返されます。このオブジェクトにはcurrentというプロパティが含まれており、ここに値を格納します。

const ref = useRef(initialValue); // 初期値を設定
console.log(ref.current); // currentプロパティにアクセス

ポイント

  • 永続性: ref.currentは、コンポーネントが再レンダーされても値を保持します。
  • 変更可能性: ref.currentは自由に読み書きが可能です。

useRefとレンダリング

useRefが保持する値は、Reactのレンダリングプロセスに影響を与えません。すなわち、useRefの値を更新しても再レンダーが発生しません。

function Example() {
  const ref = useRef(0);

  const handleClick = () => {
    ref.current += 1; // 値を更新
    console.log(ref.current); // コンソールに最新値を出力
  };

  return <button onClick={handleClick}>Click Me</button>; // 再レンダーされない
}

なぜ再レンダーされないのか?

Reactでは、コンポーネントの状態やプロパティの変更がトリガーになって再レンダーが発生します。一方で、useRefは単なる値の保管所として機能し、Reactの状態管理機構に直接関与しません。

useRefでの値の保持例

以下の例では、useRefを使って前回の値を保持し、変更の比較に利用しています。

import { useRef, useEffect } from "react";

function CompareValues({ newValue }) {
  const previousValue = useRef(null);

  useEffect(() => {
    if (previousValue.current !== null) {
      console.log(`前回の値: ${previousValue.current}, 現在の値: ${newValue}`);
    }
    previousValue.current = newValue; // 新しい値を保持
  }, [newValue]);

  return <div>現在の値: {newValue}</div>;
}

useRefでの複数値の保持

useRefを配列やオブジェクトと組み合わせることで、複数の値を管理できます。

function MultiValues() {
  const values = useRef({ count: 0, name: "John" });

  const updateValues = () => {
    values.current.count += 1;
    values.current.name = "Doe";
    console.log(values.current); // 最新のオブジェクトを出力
  };

  return <button onClick={updateValues}>Update Values</button>;
}

useRefで値を保持する仕組みのまとめ

  • useRefcurrentプロパティを通じて値を永続的に保持します。
  • 値の変更は再レンダーを引き起こさないため、パフォーマンスが向上します。
  • 状態管理が必要ない一時的なデータや再レンダー不要のデータ保持に最適です。

これらの特性を理解し、useRefを適切に活用することで、より効率的なReactアプリケーションを構築できます。

useRefを使ったコンポーネント間の値の共有

Reactでコンポーネント間で値を共有するには、通常、状態管理(useState)やContext APIを使用します。しかし、useRefを活用すると、特定の状況下で効率的に値を共有することができます。本節では、useRefを使って値をコンポーネント間で共有する方法を具体例を交えて解説します。

1. 親コンポーネントを介した値の共有

useRefを親コンポーネントで宣言し、その参照を子コンポーネントに渡すことで、値を共有できます。

import React, { useRef } from "react";

function Parent() {
  const sharedRef = useRef("初期値");

  return (
    <div>
      <ChildA refValue={sharedRef} />
      <ChildB refValue={sharedRef} />
    </div>
  );
}

function ChildA({ refValue }) {
  const updateValue = () => {
    refValue.current = "ChildAで更新";
    console.log("ChildAの値:", refValue.current);
  };

  return <button onClick={updateValue}>ChildAで値を変更</button>;
}

function ChildB({ refValue }) {
  const showValue = () => {
    console.log("ChildBの値:", refValue.current);
  };

  return <button onClick={showValue}>ChildBで値を表示</button>;
}

動作のポイント

  1. sharedRefは親コンポーネントで作成されます。
  2. 子コンポーネントにpropsで渡され、値を読み書きできます。
  3. refValue.currentに格納される値は両方の子コンポーネントで共有されます。

2. フォワーディングリファレンスを使った値の共有

ReactのforwardRefを利用して、より柔軟に値を共有できます。この方法では、親から子に渡したrefを使って値を操作します。

import React, { useRef, forwardRef, useImperativeHandle } from "react";

const Child = forwardRef((props, ref) => {
  const localValue = useRef("初期値");

  useImperativeHandle(ref, () => ({
    getValue: () => localValue.current,
    setValue: (newValue) => {
      localValue.current = newValue;
    },
  }));

  return <div>Childコンポーネント</div>;
});

function Parent() {
  const childRef = useRef();

  const handleUpdate = () => {
    childRef.current.setValue("更新された値");
    console.log("更新後の値:", childRef.current.getValue());
  };

  return (
    <div>
      <Child ref={childRef} />
      <button onClick={handleUpdate}>値を更新</button>
    </div>
  );
}

動作のポイント

  1. forwardRefを使うことで、親コンポーネントが子コンポーネントの内部値にアクセス可能。
  2. useImperativeHandleで、refを通じて子コンポーネントの関数や値を操作できます。

3. 実践例: フォーム状態の同期

複数のフォームコンポーネントで値を同期する例です。

function ParentForm() {
  const sharedRef = useRef({ name: "", email: "" });

  return (
    <div>
      <NameInput refValue={sharedRef} />
      <EmailInput refValue={sharedRef} />
      <button onClick={() => console.log(sharedRef.current)}>現在の値</button>
    </div>
  );
}

function NameInput({ refValue }) {
  const handleChange = (e) => {
    refValue.current.name = e.target.value;
  };

  return <input placeholder="名前" onChange={handleChange} />;
}

function EmailInput({ refValue }) {
  const handleChange = (e) => {
    refValue.current.email = e.target.value;
  };

  return <input placeholder="メール" onChange={handleChange} />;
}

動作のポイント

  • 各子コンポーネントでsharedRefを更新することで、データが常に同期されます。
  • フォームデータがリアルタイムで集約可能です。

useRefで値を共有するメリット

  • 軽量なデータ共有: 状態管理ツールを使う必要がなく、シンプルに値を共有可能。
  • リアクティブな動作が不要: 再レンダーを伴わない値の同期が実現。

useRefを使った値の共有は、Reactの状態管理を補完する便利な手法です。適切に利用することで、開発効率を大幅に向上させることができます。

React Contextとの比較

useRefとReactのContext APIは、どちらもコンポーネント間でデータを共有するために利用されます。ただし、使い方や適用する場面が異なります。本節では、これらのツールを比較し、それぞれの適切な使い分けについて解説します。

1. useRefとContext APIの違い

特徴useRefContext API
主な用途再レンダー不要の値の保持グローバルな状態の共有
値の変更時の再レンダー再レンダーされない再レンダーされる
データの規模小規模で一時的なデータ中〜大規模なデータ
使用場面リアクティブではない参照アプリ全体の状態共有
実装の複雑さシンプルやや複雑

2. useRefを選ぶべき場合

useRefは以下のようなシナリオで最適です。

再レンダー不要な値の保持

useRefで値を更新しても再レンダーが発生しないため、以下のようなケースで便利です:

  • カウントや一時的なデータの保持。
  • DOM要素や非同期処理の結果の一時保存。

例: useRefを利用して、非同期処理中の状態を管理

function Example() {
  const isMounted = useRef(true);

  useEffect(() => {
    return () => {
      isMounted.current = false; // アンマウント時にフラグを更新
    };
  }, []);

  const fetchData = async () => {
    const result = await fetch("/api/data");
    if (isMounted.current) {
      console.log("データを処理します:", result);
    }
  };

  return <button onClick={fetchData}>データ取得</button>;
}

3. Context APIを選ぶべき場合

Context APIは、以下のシナリオで適しています。

グローバルな状態管理

アプリ全体や多くのコンポーネント間でデータを共有したい場合に最適です。

例: テーマの状態管理

import React, { createContext, useContext, useState } from "react";

const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

function ThemeToggler() {
  const { theme, setTheme } = useContext(ThemeContext);

  const toggleTheme = () => {
    setTheme(theme === "light" ? "dark" : "light");
  };

  return <button onClick={toggleTheme}>現在のテーマ: {theme}</button>;
}

リストで状態を管理

アプリ内の大規模なデータ(例: ユーザー情報、認証状態など)を効率的に共有する場合に有効です。

4. 組み合わせて使うケース

useRefContext APIを組み合わせて使用することで、両者の利点を活かしたデータ管理が可能です。

例: 状態の参照と共有の組み合わせ

import React, { createContext, useRef, useContext } from "react";

const RefContext = createContext();

function Parent() {
  const sharedRef = useRef(0);

  return (
    <RefContext.Provider value={sharedRef}>
      <ChildA />
      <ChildB />
    </RefContext.Provider>
  );
}

function ChildA() {
  const ref = useContext(RefContext);

  const increment = () => {
    ref.current += 1;
  };

  return <button onClick={increment}>Increment</button>;
}

function ChildB() {
  const ref = useContext(RefContext);

  const showValue = () => {
    console.log("現在の値:", ref.current);
  };

  return <button onClick={showValue}>Show Value</button>;
}

5. 使い分けのまとめ

  • useRef: 再レンダーを避けたい一時的な値や参照の保持に最適。
  • Context API: アプリ全体での状態共有や更新のトリガーが必要な場合に利用。

適切に使い分けることで、効率的かつ直感的にReactアプリケーションを設計することができます。

よくあるトラブルとその対処法

useRefは非常に便利なツールですが、正しく利用しないと予期しない問題に直面することがあります。本節では、useRefを使用する際に起こりがちなトラブルと、その解決方法について解説します。

1. 値が即座に反映されない

useRefで管理している値が更新されても、その変更がリアクティブに反映されない点に注意が必要です。

問題例

function Example() {
  const countRef = useRef(0);

  const increment = () => {
    countRef.current += 1;
    console.log("最新の値:", countRef.current); // 更新された値
  };

  return (
    <div>
      <button onClick={increment}>Increment</button>
      <p>現在の値: {countRef.current}</p> {/* 表示は更新されない */}
    </div>
  );
}

解決策

useStateと組み合わせて値を反映させます。useRefは非リアクティブなデータ保持に限定して使用します。

function Example() {
  const countRef = useRef(0);
  const [displayValue, setDisplayValue] = React.useState(0);

  const increment = () => {
    countRef.current += 1;
    setDisplayValue(countRef.current); // 値を更新して表示もリフレッシュ
  };

  return (
    <div>
      <button onClick={increment}>Increment</button>
      <p>現在の値: {displayValue}</p>
    </div>
  );
}

2. `useRef`の初期値が意図しない動作を引き起こす

useRefの初期値を適切に設定しないと、コードが予期せぬ動作をすることがあります。

問題例

初期値を設定せずに操作しようとすると、undefinedエラーが発生する場合があります。

function Example() {
  const myRef = useRef(); // 初期値を設定しない

  const handleClick = () => {
    console.log(myRef.current); // undefinedのためエラー
  };

  return <button onClick={handleClick}>Check Ref</button>;
}

解決策

useRefには明確な初期値を設定します。必要に応じてnullや空のオブジェクトを利用します。

function Example() {
  const myRef = useRef(null);

  const handleClick = () => {
    console.log(myRef.current); // nullまたは適切な値が出力される
  };

  return <button onClick={handleClick}>Check Ref</button>;
}

3. DOM要素が正しく参照できない

DOM要素を参照する際に、useRefを設定する順序が間違っていると、意図した挙動をしないことがあります。

問題例

条件によってrefが正しく設定されない場合があります。

function Example() {
  const inputRef = useRef();

  const focusInput = () => {
    inputRef.current.focus(); // エラー: currentがundefined
  };

  return (
    <div>
      <button onClick={focusInput}>Focus Input</button>
      {/* Inputがまだrenderされていない可能性がある */}
      <input ref={inputRef} type="text" />
    </div>
  );
}

解決策

Reactのレンダーサイクルを理解し、useEffectを活用して初期化後に操作するようにします。

function Example() {
  const inputRef = useRef();

  useEffect(() => {
    inputRef.current.focus(); // コンポーネントマウント後にフォーカスを設定
  }, []);

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

4. 不必要に`useRef`を使用している

useRefが必要ない場面でも使われていると、コードが複雑化しやすいです。

問題例

useStateだけで十分な場面でuseRefを利用している。

function Counter() {
  const countRef = useRef(0);

  const increment = () => {
    countRef.current += 1;
    console.log("Count:", countRef.current);
  };

  return <button onClick={increment}>Increment</button>;
}

解決策

シンプルな状態管理にはuseStateを使用します。

function Counter() {
  const [count, setCount] = React.useState(0);

  const increment = () => setCount((prev) => prev + 1);

  return <button onClick={increment}>Increment</button>;
}

5. 不要な再レンダーの発生

useRefを用いたデータ共有が誤った方法で実装されると、パフォーマンスが低下する場合があります。

解決策

  • 再レンダーを最小化したい場合、useRefでデータを管理します。
  • 状態変更が必要ない一時的なデータにはuseRefを使い、状態管理にはuseStateを適切に併用します。

まとめ

useRefを正しく利用することで、Reactの状態管理を効率化し、不要な再レンダーを回避できます。しかし、誤用すると予期しない動作やエラーの原因になります。本節で紹介したトラブルとその対処法を参考に、安全かつ効果的にuseRefを活用しましょう。

応用例: フォームの状態管理

useRefは、フォームデータの一時的な状態を管理する際に非常に有効です。通常、useStateを使用してリアクティブな状態管理を行いますが、再レンダーを必要としない場合やパフォーマンスを重視する場合にはuseRefが適しています。本節では、useRefを使ったフォーム管理の具体例を解説します。

1. シンプルなフォームデータ管理

以下の例では、useRefを使ってフォームデータを管理し、再レンダーを回避しています。

import React, { useRef } from "react";

function SimpleForm() {
  const formRef = useRef({
    name: "",
    email: "",
  });

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    formRef.current[name] = value; // フォームデータを直接更新
  };

  const handleSubmit = () => {
    console.log("送信されたデータ:", formRef.current); // 現在のフォームデータを表示
  };

  return (
    <div>
      <input
        type="text"
        name="name"
        placeholder="名前"
        onChange={handleInputChange}
      />
      <input
        type="email"
        name="email"
        placeholder="メール"
        onChange={handleInputChange}
      />
      <button onClick={handleSubmit}>送信</button>
    </div>
  );
}

この方法のメリット

  • 再レンダーを最小限に抑えることが可能。
  • 大量のフォームフィールドを扱う場合にもパフォーマンスが向上。

2. 入力フィールドへのフォーカス制御

useRefを使用してフォーム入力フィールドに直接アクセスし、フォーカスを動的に設定します。

function FormWithFocus() {
  const nameInputRef = useRef(null);
  const emailInputRef = useRef(null);

  const focusEmail = () => {
    emailInputRef.current.focus(); // メール入力フィールドにフォーカス
  };

  return (
    <div>
      <input ref={nameInputRef} type="text" placeholder="名前" />
      <input ref={emailInputRef} type="email" placeholder="メール" />
      <button onClick={focusEmail}>メールにフォーカス</button>
    </div>
  );
}

この方法のメリット

  • ユーザーエクスペリエンスの向上。
  • カスタムバリデーションやステップバイステップのフォーム入力に応用可能。

3. 動的なフォームフィールドの管理

動的に追加されるフォームフィールドの値をuseRefで管理する例です。

function DynamicForm() {
  const fieldsRef = useRef([]);

  const addField = () => {
    fieldsRef.current.push({ value: "" });
  };

  const handleInputChange = (index, value) => {
    fieldsRef.current[index].value = value; // 指定されたフィールドの値を更新
  };

  const handleSubmit = () => {
    console.log("送信されたデータ:", fieldsRef.current);
  };

  return (
    <div>
      <button onClick={addField}>フィールドを追加</button>
      {fieldsRef.current.map((field, index) => (
        <input
          key={index}
          placeholder={`フィールド${index + 1}`}
          onChange={(e) => handleInputChange(index, e.target.value)}
        />
      ))}
      <button onClick={handleSubmit}>送信</button>
    </div>
  );
}

この方法のメリット

  • 動的なフォーム構造に柔軟に対応可能。
  • useStateを使わないため、大量のフィールドでもパフォーマンスが向上。

4. バリデーションの実装

useRefを使ってフォームデータを保持しつつ、カスタムバリデーションを実装します。

function FormWithValidation() {
  const formRef = useRef({
    name: "",
    email: "",
  });

  const validateForm = () => {
    const { name, email } = formRef.current;
    if (!name || !email) {
      alert("すべてのフィールドを入力してください。");
      return false;
    }
    if (!/\S+@\S+\.\S+/.test(email)) {
      alert("正しいメールアドレスを入力してください。");
      return false;
    }
    return true;
  };

  const handleSubmit = () => {
    if (validateForm()) {
      console.log("送信されたデータ:", formRef.current);
    }
  };

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    formRef.current[name] = value;
  };

  return (
    <div>
      <input
        type="text"
        name="name"
        placeholder="名前"
        onChange={handleInputChange}
      />
      <input
        type="email"
        name="email"
        placeholder="メール"
        onChange={handleInputChange}
      />
      <button onClick={handleSubmit}>送信</button>
    </div>
  );
}

この方法のメリット

  • 再レンダーなしでリアルタイムにバリデーションを実行可能。
  • ユーザーの入力エラーを即座に検出。

まとめ

useRefを使用すると、フォームデータの管理や操作を効率的に行うことができます。再レンダーを最小限に抑えることでパフォーマンスを向上させ、大規模なフォームや動的なフォーム構造にも対応可能です。これらの応用例を参考に、柔軟なフォーム管理を実現しましょう。

演習問題: 実践的な練習

useRefを使って値を管理し、コンポーネント間で共有する方法を学んだところで、実際にコードを書いて練習してみましょう。以下の課題に取り組むことで、useRefの使い方を深く理解できます。


演習1: クリックカウントの管理

以下の要件を満たすコードを作成してください。

要件:

  1. 親コンポーネントでuseRefを使用して、クリック回数を保持します。
  2. 2つの子コンポーネントがあり、1つはクリック回数をインクリメントするボタン、もう1つは現在のクリック回数を表示するボタンを持ちます。
  3. クリック回数は再レンダーされることなく保持されるようにしてください。

期待される動作:

  • 「Increment」ボタンをクリックするとカウントが増加します。
  • 「Show Count」ボタンをクリックすると現在のカウントがコンソールに表示されます。

演習2: フォームデータの管理

以下の要件を満たすコードを作成してください。

要件:

  1. フォームコンポーネントで、useRefを使用して名前とメールアドレスの値を保持します。
  2. 「送信」ボタンをクリックした際、フォームデータをコンソールに表示します。
  3. フォームフィールドの値が変更されても再レンダーが発生しないようにしてください。

期待される動作:

  • フォームに入力後、「送信」をクリックすると入力データが出力されます。
  • 再レンダーが発生せず、パフォーマンスを維持します。

演習3: 動的フォームの作成

以下の要件を満たすコードを作成してください。

要件:

  1. 親コンポーネントで、useRefを使用してフォームフィールドを動的に追加・管理します。
  2. 「フィールドを追加」ボタンをクリックすると、新しい入力フィールドが追加されます。
  3. 各フィールドの値を保持し、「送信」ボタンをクリックした際にすべてのデータをコンソールに出力します。

期待される動作:

  • 入力フィールドを動的に追加できます。
  • すべてのフィールドのデータを一括で取得できます。

模範解答

課題に取り組んだ後、以下の模範解答を参考にしてください。

演習1:

import React, { useRef } from "react";

function Parent() {
  const clickCount = useRef(0);

  return (
    <div>
      <ChildA countRef={clickCount} />
      <ChildB countRef={clickCount} />
    </div>
  );
}

function ChildA({ countRef }) {
  const increment = () => {
    countRef.current += 1;
  };

  return <button onClick={increment}>Increment</button>;
}

function ChildB({ countRef }) {
  const showCount = () => {
    console.log("現在のクリック数:", countRef.current);
  };

  return <button onClick={showCount}>Show Count</button>;
}

演習2:

function Form() {
  const formData = useRef({ name: "", email: "" });

  const handleInputChange = (e) => {
    formData.current[e.target.name] = e.target.value;
  };

  const handleSubmit = () => {
    console.log("送信データ:", formData.current);
  };

  return (
    <div>
      <input name="name" placeholder="名前" onChange={handleInputChange} />
      <input name="email" placeholder="メール" onChange={handleInputChange} />
      <button onClick={handleSubmit}>送信</button>
    </div>
  );
}

演習3:

function DynamicForm() {
  const fields = useRef([]);

  const addField = () => {
    fields.current.push({ value: "" });
  };

  const handleInputChange = (index, value) => {
    fields.current[index].value = value;
  };

  const handleSubmit = () => {
    console.log("送信データ:", fields.current);
  };

  return (
    <div>
      <button onClick={addField}>フィールドを追加</button>
      {fields.current.map((field, index) => (
        <input
          key={index}
          placeholder={`フィールド${index + 1}`}
          onChange={(e) => handleInputChange(index, e.target.value)}
        />
      ))}
      <button onClick={handleSubmit}>送信</button>
    </div>
  );
}

これらの練習問題を通じて、useRefの基本的な活用方法から応用的な使い方まで身につけてください!

まとめ

本記事では、ReactのuseRefを用いて値を効率的に管理し、コンポーネント間で共有する方法について解説しました。useRefの基本的な仕組みから、他の状態管理手法との違い、実践的な応用例まで幅広く取り上げました。

特に、再レンダーのトリガーを避けつつ値を保持する特性は、フォーム管理や動的データの操作、パフォーマンス重視の開発において大きな利点となります。また、演習問題を通じて、useRefの効果的な使い方を実践的に学ぶことができました。

正しくuseRefを活用することで、Reactアプリケーションの効率性と柔軟性を向上させることができます。ぜひ今回の内容を活かして、さらなる開発の最適化に役立ててください!

コメント

コメントする

目次
  1. useRefの基本的な理解
    1. useRefの仕組み
    2. 主な特徴
    3. 主な用途
  2. useRefを使用するメリット
    1. 1. 再レンダーを回避
    2. 2. 値の永続性
    3. 3. 状態管理の簡略化
    4. 4. DOM要素へのアクセス
    5. 5. カスタムフックでの応用
  3. useRefで値を保持する仕組み
    1. useRefの基本構造
    2. useRefとレンダリング
    3. useRefでの値の保持例
    4. useRefでの複数値の保持
    5. useRefで値を保持する仕組みのまとめ
  4. useRefを使ったコンポーネント間の値の共有
    1. 1. 親コンポーネントを介した値の共有
    2. 2. フォワーディングリファレンスを使った値の共有
    3. 3. 実践例: フォーム状態の同期
    4. useRefで値を共有するメリット
  5. React Contextとの比較
    1. 1. useRefとContext APIの違い
    2. 2. useRefを選ぶべき場合
    3. 3. Context APIを選ぶべき場合
    4. 4. 組み合わせて使うケース
    5. 5. 使い分けのまとめ
  6. よくあるトラブルとその対処法
    1. 1. 値が即座に反映されない
    2. 2. `useRef`の初期値が意図しない動作を引き起こす
    3. 3. DOM要素が正しく参照できない
    4. 4. 不必要に`useRef`を使用している
    5. 5. 不要な再レンダーの発生
    6. まとめ
  7. 応用例: フォームの状態管理
    1. 1. シンプルなフォームデータ管理
    2. 2. 入力フィールドへのフォーカス制御
    3. 3. 動的なフォームフィールドの管理
    4. 4. バリデーションの実装
    5. まとめ
  8. 演習問題: 実践的な練習
    1. 演習1: クリックカウントの管理
    2. 演習2: フォームデータの管理
    3. 演習3: 動的フォームの作成
    4. 模範解答
  9. まとめ