React HooksのuseEffectの使い方!第2引数の方法もまとめ!





けど、そんな中で悩むことは、

・React HooksのuseEffectの使い方がわからない
・React HooksのuseEffectの第2引数で制御する方法がわからならい

ですよね。

今回はそんなお悩みを解決する

・React HooksのuseEffectの使い方
・React HooksのuseEffectの第2引数で制御する方法

についてまとめます!

React HooksのuseEffectのとは

useEffectは、React Hooksの関数コンポーネントで、関数をマウントやレンダリング後のタイミングに応じて実行することができます。
具体的な処理内容はDOMへの入出力、オブジェクトやプロパティの代入や配列操作、外部APIとのデータ処理などとなっています。
Reactではこれらの処理を副作用と呼んでいて、副作用をきっかけに、関数として渡された処理を実行します。

タカヒロ
タカヒロ
useEffectは、画面上で何かの処理が行われたついでに関数の処理を制御できるものととらえるとわかりやすいでしょう。

React HooksのuseEffectの使い方

React HooksのuseEffectの使い方について説明をします。

React HooksのuseEffectの構文と引数

React HooksのuseEffectの構文と引数は以下の通りです。

React HooksのuseEffectの構文

useEffect(<第1引数>[, <第2引数>]);

React HooksのuseEffectの引数

 

第1引数 マウントやレンダリング後に実行させたい副作用関数を指定します。
第2引数 副作用関数の実行タイミングを制御する依存データを指定します。
空の配列を記載した場合は初回レンダリング後のみ、何も記載しない場合は毎回のマウントやレンダリング後に実行されます。

React HooksのuseEffectを実装する

React HooksのuseEffectを実装するにはuseEffectをimportします。

import { useEffect, useState } from "react"

React HooksのuseEffectで登録がされたタイミングで実行する

まずは、React HooksのuseEffectで以下のTodoを入力し登録処理がされたタイミングで実行するよう設定をしていきましょう。
以下サンプルコードです。

import { useEffect, useState }  from "react"
import { useForm } from "react-hook-form";
import { collection, getDocs, addDoc } from "firebase/firestore";
import fireStoreDB from "./firebase";

function App() {
  //react-hook-form 初期設定
  const { register, handleSubmit, formState: { errors } } = useForm();

  //useState 初期設定
  const [post, setPosts] = useState("まだ登録処理はされていません");
  const [todos, setTodos] = useState([]);

  //データ取得用配列
  const arrList = [];

  //FireStoreの登録-ここから
  const fireStoreSubmit = async (data) => {
    const { title, status, shousai } = data;
    await addDoc(collection(fireStoreDB, "todoposts"), {
      title: title,
      status: status,
      shousai: shousai,
    });
    console.log(title, status, shousai);
    setPosts("登録処理がされました");
  };
  //FireStoreの登録-ここまで

  //useEffectの処理-ここから
  useEffect(() => {
    console.log("useEffectが呼ばれました")
    console.log(post);

    const fireStorePostData = collection(fireStoreDB, "todoposts");
    getDocs(fireStorePostData).then((snapShot) => {
      snapShot.forEach((docs) => {
        const doc = docs.data();
        arrList.push({  id: docs.id ,title: doc.title ,status: doc.status, shousai: doc.shousai})
      })
      setTodos(arrList);
    });
  },[post]);
  //useEffectの処理-ここまで

  return (
    <div>
      <div>
        <div>
          <form onSubmit={handleSubmit(fireStoreSubmit)}>
            <label>タイトル:</label><br/>
            <input {...register("title", { required: true })}  name="title" /><br/>
            {errors.title && <p>必須です。タイトルを入力してください。</p>}
            <label>ステータス:</label><br/>
            <select {...register("status", { required: true })}  name="status">
            <option value=''>↓ステータス選択</option>
              <option value='未着手'>未着手</option>
              <option value='作業中'>作業中</option>
              <option value='完了'>完了</option>
            </select>
            {errors.status && <p>必須です。ステータスを選択してください。</p>}<br/>
            <label>詳細:</label><br/>
            <textarea {...register("shousai", { maxLength: 10 })}  name="shousai"/>
            {errors.shousai && <p>10文字以内で入力してください。</p>}
            <br/><input type="submit" value="登録" />
		      </form>
        </div>
     </div>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <div>タイトル:{todo.title}</div>
            <div>ステータス:{todo.status}</div>
            <div>詳細:{todo.shousai}</div>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

Todoの入力フォームに値を入力し、登録するとデータベースのFireStoreへ値が登録され、その値を再取得し、画面上に表示させる処理内容となっています。

Todo入力フォームとその下にデータベース登録済みのリストが表示されます。

Todoの入力フォームに値を入力し、登録すると

データベースのFireStoreへ値が登録されます。

FireStoreのデータを再取得し、画面上に表示させます。

初回表示は必ず1回useEffectが呼び出される

初回表示は必ず1回useEffectが呼び出されます。
コードを実装後、ブラウザを立ち上げ、コンソールを確認すると初回メッセージが出力されることがわかります。

登録処理を行いuseEffectの実行タイミングを確認する

useEffectの実行タイミングを確認していきましょう。
値を入力し、登録ボタンを押して実行します。

はい、登録後にuseEffectが実行されたことがわかりますね。

データベースのFireStoreへ値が登録され、

画面上に登録した値がリスト表示されました。

コードの説明

useEffectの第二引数の関数に変数「post」を指定し、変数「post」が変化されるタイミングのみ実行するよう設定をしています。

  useEffect(() => {
   …
    });
  },[post]);

変数「post」は登録処理後にsetPostsメソッドを通じて値を代入するようにしています。

  const fireStoreSubmit = async (data) => {
   …
    setPosts("登録処理がされました");
  };

タカヒロ
タカヒロ
まずは、登録処理にuseEffectを使用した場合の理想形を先に説明しました。次はちょっと問題があるパターンでuseEffectの使用例を説明します。

React HooksのuseEffectで一度だけ処理をおこなう

React HooksのuseEffectで一度だけ処理をおこなう方法について説明をします。

同じくTodoのサンプルコードを使います。

import { useEffect, useState }  from "react"
import { useForm } from "react-hook-form";
import { collection, getDocs, addDoc } from "firebase/firestore";
import fireStoreDB from "./firebase";

function App() {
  //react-hook-form 初期設定
  const { register, handleSubmit, formState: { errors } } = useForm();

  //useState 初期設定
  const [post, setPosts] = useState("まだ登録処理はされていません");
  const [todos, setTodos] = useState([]);

  //データ取得用配列
  const arrList = [];

  //FireStoreの登録-ここから
  const fireStoreSubmit = async (data) => {
    const { title, status, shousai } = data;
    await addDoc(collection(fireStoreDB, "todoposts"), {
      title: title,
      status: status,
      shousai: shousai,
    });
    console.log(title, status, shousai);
    setPosts("登録処理がされました");
  };
  //FireStoreの登録-ここまで

  //useEffectの処理-ここから
  useEffect(() => {
    console.log("useEffectが呼ばれました")
    console.log(post);

    const fireStorePostData = collection(fireStoreDB, "todoposts");
    getDocs(fireStorePostData).then((snapShot) => {
      snapShot.forEach((docs) => {
        const doc = docs.data();
        arrList.push({  id: docs.id ,title: doc.title ,status: doc.status, shousai: doc.shousai})
      })
      setTodos(arrList);
    });
  },[]);
  //useEffectの処理-ここまで

  return (
    <div>
      <div>
        <div>
          <form onSubmit={handleSubmit(fireStoreSubmit)}>
            <label>タイトル:</label><br/>
            <input {...register("title", { required: true })}  name="title" /><br/>
            {errors.title && <p>必須です。タイトルを入力してください。</p>}
            <label>ステータス:</label><br/>
            <select {...register("status", { required: true })}  name="status">
            <option value=''>↓ステータス選択</option>
              <option value='未着手'>未着手</option>
              <option value='作業中'>作業中</option>
              <option value='完了'>完了</option>
            </select>
            {errors.status && <p>必須です。ステータスを選択してください。</p>}<br/>
            <label>詳細:</label><br/>
            <textarea {...register("shousai", { maxLength: 10 })}  name="shousai"/>
            {errors.shousai && <p>10文字以内で入力してください。</p>}
            <br/><input type="submit" value="登録" />
		      </form>
        </div>
     </div>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <div>タイトル:{todo.title}</div>
            <div>ステータス:{todo.status}</div>
            <div>詳細:{todo.shousai}</div>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

一度だけ処理を行わせる場合はuseEffectの第二引数を「[]」に設定します。

  useEffect(() => {
   …
    });
  },[]);

初回表示は必ず1回useEffectが呼び出される

初回表示は必ず1回useEffectが呼び出されます。

登録処理を行いuseEffectの実行タイミングを確認する

useEffectの実行タイミングを確認していきましょう。
値を入力し、登録ボタンを押して実行します。

前回は登録後にuseEffectが実行されましたが、今回は実行されませんね。

ただ、useEffectではないsubmit処理は行われているので、
データベースのFireStoreへ値が登録されています。

useEffect内に記載されているデータの再取得処理は行われていないので、
画面上にのリストは変わらない結果となります。

タカヒロ
タカヒロ
このパターンの処理は画面描画後の一度だけの実行になることに注意しましょう。なおこの方法はデータベースなど外部データのアクセスを極力抑えたい場合に有効とも言えますね。

1度だけ実行されるはずが2回実行されてしまう場合

useEffectの設定で1度だけ実行されるはずが2回実行されてしまう場合は、バグを踏んでいる可能性がありますので、
以下の記事にて確認をしてみてください。

Reactで初回useEffectが2回呼ばれる原因と対処法!

React HooksのuseEffectで画面上の処理が行われる都度で処理をおこなう

React HooksのuseEffectで画面上の処理が行われる都度で処理をおこなう方法について説明をします。

同じくTodoのサンプルコードを使います。

import { useEffect, useState }  from "react"
import { useForm } from "react-hook-form";
import { collection, getDocs, addDoc } from "firebase/firestore";
import fireStoreDB from "./firebase";

function App() {
  //react-hook-form 初期設定
  const { register, handleSubmit, formState: { errors } } = useForm();

  //useState 初期設定
  const [post, setPosts] = useState("まだ登録処理はされていません");
  const [todos, setTodos] = useState([]);

  //データ取得用配列
  const arrList = [];

  //FireStoreの登録-ここから
  const fireStoreSubmit = async (data) => {
    const { title, status, shousai } = data;
    await addDoc(collection(fireStoreDB, "todoposts"), {
      title: title,
      status: status,
      shousai: shousai,
    });
    console.log(title, status, shousai);
    setPosts("登録処理がされました");
  };
  //FireStoreの登録-ここまで

  //useEffectの処理-ここから
  useEffect(() => {
    console.log("useEffectが呼ばれました")
    console.log(post);

    const fireStorePostData = collection(fireStoreDB, "todoposts");
    getDocs(fireStorePostData).then((snapShot) => {
      snapShot.forEach((docs) => {
        const doc = docs.data();
        arrList.push({  id: docs.id ,title: doc.title ,status: doc.status, shousai: doc.shousai})
      })
      //setTodos(arrList);
    });
  },);
  //useEffectの処理-ここまで

  return (
    <div>
      <div>
        <div>
          <form onSubmit={handleSubmit(fireStoreSubmit)}>
            <label>タイトル:</label><br/>
            <input {...register("title", { required: true })}  name="title" /><br/>
            {errors.title && <p>必須です。タイトルを入力してください。</p>}
            <label>ステータス:</label><br/>
            <select {...register("status", { required: true })}  name="status">
            <option value=''>↓ステータス選択</option>
              <option value='未着手'>未着手</option>
              <option value='作業中'>作業中</option>
              <option value='完了'>完了</option>
            </select>
            {errors.status && <p>必須です。ステータスを選択してください。</p>}<br/>
            <label>詳細:</label><br/>
            <textarea {...register("shousai", { maxLength: 10 })}  name="shousai"/>
            {errors.shousai && <p>10文字以内で入力してください。</p>}
            <br/><input type="submit" value="登録" />
		      </form>
        </div>
     </div>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <div>タイトル:{todo.title}</div>
            <div>ステータス:{todo.status}</div>
            <div>詳細:{todo.shousai}</div>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

随時処理を行わせる場合はuseEffectの第二引数を空に設定します。

  useEffect(() => {
   …
    });
  },);

またuseEffectの中にある処理はいったん無効化します。

 //setTodos(arrList);

タカヒロ
タカヒロ
setTodos自体関数であり、関数が実行するタイミングで処理が行われる結果、ループ状態となるので、無効化しています。

初回表示は必ず1回useEffectが呼び出される

初回表示は必ず1回useEffectが呼び出されます。

登録処理を行いuseEffectの実行タイミングを確認する

useEffectの実行タイミングを確認していきましょう。
値を入力しないで、登録ボタンを押して実行します。

react-hook-formコンポーネントによるエラーチェック処理が実行され、

そのタイミングでuseEffectが実行されることがわかりますね。

タカヒロ
タカヒロ
第二引数なしのuseEffectは大量に外部データへのアクセスが行われる形になり、パフォーマンスに影響が出る可能性が高いです。また無限ループを引き起こすリスクがありますので、必ず第二引数を指定するようにしましょう。

さいごに

いかがでしょうか。

今回は、

・React HooksのuseEffectの使い方
・React HooksのuseEffectの第2引数で制御する方法

についてまとめました。

また、他にも便利な方法がありますので、よろしければご参照頂ければと思います。


タカヒロ
タカヒロ
Reactって覚えることが多く難しいですよね...
タカヒロも悩んでいましたが、こちらの本でかなり理解ができるようになりました!


>>Amazonで詳細を見る


電子書籍もいいですが、紙質がやわらかく読みやすいので書籍のほうがおすすめです。


この記事の関連キーワード

こちらの記事の関連キーワード一覧です。クリックするとキーワードに関連する記事一覧が閲覧できます。