🐕

【useEffect】副作用フックの使い方

2021/09/27に公開

概要

副作用フックの使い方を下記の公式ドキュメントの例を用いて説明します。
下記の例はカウンターを表示します。ボタンをクリックすると、カウンターの値が増えます。また、クリック回数を含んだカスタムのメッセージをドキュメントのタイトルに設定しています。
https://ja.reactjs.org/docs/hooks-effect.html

useEffectとは関数を渡すことでレンダリング時に渡した関数を処理することができます。
React コンポーネントにおける副作用フックには 2 種類あります。クリーンアップコードを必要としない副作用と、必要とする副作用です。これらの違いについて詳しく見ていきましょう。

クリーンアップを必要としない副作用

useEffectにコールバック関数を渡すことでレンダリング後に毎回渡した関数が処理されます。useEffectはデフォルトでは、副作用関数は初回のレンダー時および毎回の更新時に呼び出されます。あとでカスタマイズする方法について説明します。stateの値が更新されて再レンダリングされる度に実行されます。

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

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

上記のコードの下記部分がuseEffectの部分になります。

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

上記のコードにより、レンダリング後に毎回 document.title の値を変更します。

クリーンアップを有する副作用

クリーンアップとはコンポーネントをアンマウントする時の処理を設定することができます。
設定する方法はuseEffectに渡すコールバック関数にreturnで処理を書くとその処理がアンマウント時に処理が実行されます。

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

上記のコードで、レンダリング後に毎回ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange)が実行され、アンマウント時にcleanup()が実行されます。

Tipsまとめ

よく使うであろうuseEffectのTipsを説明します。

関心を分離するために複数の副作用を使う

useEffectは1つの内で複数回呼ぶことができます。なので処理の塊を分けることができます。関係のない処理は分けることで可読性が上がります。

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  // ...
}

副作用のスキップによるパフォーマンス改善

useEffectの第2引数で配列を渡すことができます。
useEffectはデフォルトではレンダリング後に毎回処理が実行されますが、第2引数を渡すことでそのタイミングを制御することができます。

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]);

上記のコードを例に説明します。
第2引数にcountを渡しているので、レンダリング時のcountの値を記憶されます。
次回のレンダリング時に前回のレンダリングと値を比較して値が同じだった場合useEffect内の処理がスキップされてます。つまり、初回のレンダリング時とcountの値が変わった場合のみ処理されるようになります。

以上です。読んでいただきありがとうございました。
誤字、脱字、間違い等ありましたらコメント頂ければ幸いです。

Discussion