LoginSignup
29
25

More than 1 year has passed since last update.

React hooksのuseEffectについて理解したい

Last updated at Posted at 2022-09-08

useEffectとは?

useEffect は何をやっているのか? このフックを使うことで、レンダー後に何かの処理をしないといけない、ということを React に伝えます。React はあなたが渡した関数を覚えており(これを「副作用(関数)」と呼ぶこととします)、DOM の更新の後にそれを呼び出します。この副作用の場合はドキュメントのタイトルをセットしていますが、データを取得したりその他何らかの命令型の API を呼び出したりすることも可能です。

つまりuseEffectは、Reactのレンダリング後に関数を実行することができるフックということです。

関数コンポーネント内で副作用の処理(DOM操作、変数の代入、API通信などUI構築以外の処理)を扱え、実行することができます。

またuseEffectは、ReactのClassコンポーネントのライフサイクル
componentDidMount, componentDidUpdate、componentWillUnmount
の3つと同様な処理を行うことができます。

useEffectを色々試してみる

レンダリング後にuseEffectを実行する

import React, { useState, useEffect } from 'react';

export const UseEffectExample = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const text = document.getElementById("text");
    if(text) {
      text.innerHTML = `useEffectの値は${(count * 3)} です`
    }
  });

  return (
    <div>
      <p>ボタンが {count} 回押されました</p>
      <button onClick={() => setCount(count + 1)}>
        ボタン
      </button>
      <p id="text"></p>
    </div>
  )
};

まずはレンダリング後にuseEffectを実行する方法です。

onClickされるとsetCountでcountが増えて行きます。
この際に再レンダリングされるのでuseEffectも実行され、ボタン下のpタグに増えた分のcountを3倍した値が表示されるという流れになります。

image.png

最初のマウント時だけuseEffectを実行する

useEffectの第二引数に空配列を渡します。

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

それ以外は、さっきのコードと同じです。

import React, { useState, useEffect } from 'react';

export const UseEffectExample = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const text = document.getElementById("text");
    if(text) {
      text.innerHTML = `useEffectの値は${(count + 1)} です`
    }
  }, []);

  return (
    <div>
      <p>ボタンが {count} 回押されました</p>
      <button onClick={() => setCount(count + 1)}>
        ボタン
      </button>
      <p id="text"></p>
    </div>
  )
};

このように再レンダリングされても、useEffectでのカウントは増えません。

image.png

値の変更時にuseEffectを実行する

値が変化したときにuseEffectを実行したい場合もあるかと思います。
そんな時は、useEffectの第二引数の配列にその値を渡すことで実現できます。

useEffect(() => { ... }, [変数1, 変数2, ...]);
import React, { useState, useEffect } from 'react';

export const UseEffectExample = () => {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  useEffect(() => {
    const text = document.getElementById("text");
    if(text) {
      text.innerHTML = `useEffectの値は${(count * 5)} です`
    }
  }, [flag]);

  return (
    <div>
      <p>ボタンが {count} 回押されました</p>
      <button onClick={() => setCount(count + 1)}>
        ボタン
      </button>
      <button onClick={() => setFlag(true)}>
        ボタン2
      </button>
      <p id="text"></p>
    </div>
  )
};

ボタン2はfalseの状態のflagをtrueに変えるだけのボタンです。

このボタンの押下で、flagの値が変化しuseEffectが実行されます。
二度目は、すでにflagはtrueとなっているため値が変わらずに、useEffectは実行されません。

image.png

無限ループに気を付ける

useEffectの第二引数を指定せずにuseStateのsetterを呼び出してしまうと、
以下のようにuseEffectの処理が無限ループします。

import React, { useState, useEffect } from 'react';

export const UseEffectExample = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(count + 1)
  });

  return (
    <div>
      <p>ボタンが {count} 回押されました</p>
      <button onClick={() => setCount(count + 1)}>
        ボタン
      </button>
      <p id="text"></p>
    </div>
  )
};

たった数秒でこのような状態に…

image.png

一応、フォーマッターなどで以下のように警告を出してくれる場合もありますが、
あまり過信せず十分気をつけましょう。

React Hook useEffect contains a call to 'setCount'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass [count] as a second argument to the useEffect

参考

29
25
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
29
25