Tyotto good!

【React Hooks】useEffectの基本的な動きを理解して使いこなそう

Posted: February 19, 2021

今回はReact Hooksの基本的なHookであるuseEffectについて説明していきます!

useEffectとは

useEffectは、関数コンポーネント内で副作用(side-effect)を実行するためのHookです。副作用とは、関数コンポーネントの出力(レンダリング)に関係ない処理のことです。つまり、useEffectを用いることでレンダリングと副作用を切り離すことが可能になります。
例えば、useEffectを使わない以下のようなコンポーネントがあるとします。

import React, { useState } from "react";

const UseEffectExample: React.FC = () => {
  const [count, setCount] = useState(0);

	if (process.browser) {
    console.log("called");
  }

  return <button onClick={() => setCount(count + 1)}>Click me</button>;
};

export default UseEffectExample;

この場合だと、副作用とレンダリングが分離されていないため、コンポーネントがレンダリングされる(クリックされる)度にlogが出力されます。
ここでuseEffectを用いることで以下のように変更できます。

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

const UseEffectExample: React.FC = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("called");
  }, []);

  return <button onClick={() => setCount(count + 1)}>Click me</button>;
};

export default UseEffectExample;

レンダリングと副作用を分離することができ、logはコンポーネントが初期にマウントされた時しか出力されません。(なぜこうなるかは後ほど説明します!)

useEffectの定義

useEffectは以下のような引数を持ちます。

useEffect(callback[, dependencies]);

第一引数には副作用の処理を記述するためのコールバック関数を持ち、これはレンダーの結果が画面に反映された後に動作します。
第二引数には依存先の変数が格納される配列が渡されます。これによって副作用の処理をどのタイミングで実行するか決めることができます。
これらについて詳細を説明していきます🙌

useEffectの実行されるタイミングを理解する

先ほど軽く説明したuseEffectの第二引数である[dependencies]を利用することで副作用の処理を実行するタイミングを制御することができます。

  • まず、第二引数を指定しない場合は、副作用は全レンダリング後に実行されます。
// レンダリングされるたびにログが出力される
useEffect(() => {
  console.log("called");
});
  • 第二引数を指定した場合、配列に格納された値が変更された場合のみ実行されます。
// レンダリング間でcountの値が変更された時のみログが出力される
useEffect(() => {
  console.log("called");
}, [count]);
  • 第二引数に空配列が渡された場合、副作用は最初のレンダリングが行われた後だけ1回実行されます。最初に説明した例はこちらの記述をしていたため、コンポーネントがマウントされた初回のみログを出力していたのですね。
// 最初にレンダリングされる時のみログが出力される
useEffect(() => {
    console.log("called");
}, []);

副作用のクリーンアップ

副作用の中には、タイマーIDやsubscriptionなど、コンポーネントがアンマウントされる時にクリーンアップする必要があります。そのような時にuseEffect内で関数を返すことでそれをクリーンアップの処理とみなすことができます。
以下に例を示します。

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

const UseEffectExample2: React.FC = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      console.log(count);
    }, 2000);
    return () => {
      clearInterval(id);
    };
  }, [count]);

  return <button onClick={() => setCount(count + 1)}>Click me</button>;
};

export default UseEffectExample2;

useEffect内でsetIntervalを使用しており、毎秒ログを出力していることがわかります。
また、useEffect内で関数を返し、その中でクリーンアップ処理を書くことができるので、clearIntervalでクリーンアップ処理をしています。

まとめ

  • useEffectは、関数コンポーネントの出力(レンダリング)に関係ない処理である副作用をレンダリングと切り離して扱うことができる
  • useEffectの第二引数に依存する値の配列を渡すことで、副作用の処理が実行されるタイミングを制御できる
  • useEffect内で関数を返すことでクリーンアップ処理を行うことができる

さらにuseEffectについて深く理解したい方は、次にこの記事を読むことをオススメします!