react hooksのuseEffect実装3パターン

useEffectは、第二引数に渡す値によって動作が変わってくる。ここさえ掴んでしまえば結構便利な機能だと思う。

第二引数に空の配列を使用するパターン

コンポーネントがマウント(DOMへ挿入≒表示)された時とアンマウント(DOMから削除)された時にそれぞれ1度のみ処理を実行するパターン。

・マウント時にイベントハンドラを登録しアンマウント時に解除する
・subscribe/unsubscribe型のサービスを使用するため、マウント時にsubscribeしアンマウント時にunsubscrieする
・マウント時にwebAPIを呼び出す

など、意外と用途は多いだろう。
(引数に空の配列を指定する場合と省略する場合では動作が異なることに注意)

App.tsx

import React, { useState, useCallback, useEffect } from 'react';
import './App.css';

function App() {
  const [countA, setCountA] = useState(0);
  const [countB, setCountB] = useState(0);

  useEffect(() => {
    // コンポーネントのマウント時に1度のみ呼び出される
    console.log("mount");
    // このreturn文は不要なら省略可能
    return () => {
      // コンポーネントのアンマウント時に1度のみ呼び出される
      console.log("unmount");
    }
  },
  []);

  const onButtonAClick = useCallback(() => {
    setCountA(c => c + 1);
  }, []);

  const onButtonBClick = useCallback(() => {
    setCountB(c => c + 1);
  }, []);

  return (
    <div className="App">
      <div>
        <button onClick={onButtonAClick}>BUTTONA</button>
        <button onClick={onButtonBClick}>BUTTONB</button>
      </div>
      <div>BUTTONAが{countA}回押されました</div>
      <div>BUTTONBが{countB}回押されました</div>
    </div>
  );
}

export default App;

このコードの場合、このコンポーネントがマウントされた時点でコンソールに”mount”と表示され、アンマウントされた時点で”unmount”と表示される。

第二引数を省略するパターン

レンダリングの度に処理を行うパターン。どうしても必要ない限りは避けたほうがよいだろう。
(eslintを使用していると、実装によっては更新の無限チェーンが発生するかもなどと警告を受ける)

import React, { useState, useCallback, useEffect } from 'react';
import './App.css';

function App() {
  const [countA, setCountA] = useState(0);
  const [countB, setCountB] = useState(0);

  useEffect(() => {
    // レンダリングごとに呼び出される
    console.log("mount");
    // このreturn文は不要なら省略可能
    return () => {
      // 次回のレンダリング、もしくはコンポーネントのアンマウント時に呼び出される
      console.log("unmount");
    }
  });

  const onButtonAClick = useCallback(() => {
    setCountA(c => c + 1);
  }, []);

  const onButtonBClick = useCallback(() => {
    setCountB(c => c + 1);
  }, []);

  return (
    <div className="App">
      <div>
        <button onClick={onButtonAClick}>BUTTONA</button>
        <button onClick={onButtonBClick}>BUTTONB</button>
      </div>
      <div>BUTTONAが{countA}回押されました</div>
      <div>BUTTONBが{countB}回押されました</div>
    </div>
  );
}

export default App;

このコードの場合、このコンポーネントがマウントされた時点でコンソールに”mount”と表示される。以降BUTTONAやBUTTONBをクリックするたびに再レンダリングが走り、”unmount”->”mount”の順に表示される。

第二引数に変数の配列を指定するパターン

第二引数に指定した変数を前回のものと比較し、変化があった場合にのみ処理を行うパターン。
無駄なレンダリングやレンダリングのループを防ぎやすいので、第二引数を省略するよりはこちらのほうが良いだろう

import React, { useState, useCallback, useEffect } from 'react';
import './App.css';

function App() {
  const [countA, setCountA] = useState(0);
  const [countB, setCountB] = useState(0);

  useEffect(() => {
    // レンダリングごとに呼び出される
    console.log("mount");
    // このreturn文は不要なら省略可能
    return () => {
      // 次回のレンダリング、もしくはコンポーネントのアンマウント時に呼び出される
      console.log("unmount");
    }
  },
  // レンダリング時にcountAを確認し、前回の値から変化していない場合は実行をスキップする(複数指定可能)
  [countA]);

  const onButtonAClick = useCallback(() => {
    setCountA(c => c + 1);
  }, []);

  const onButtonBClick = useCallback(() => {
    setCountB(c => c + 1);
  }, []);

  return (
    <div className="App">
      <div>
        <button onClick={onButtonAClick}>BUTTONA</button>
        <button onClick={onButtonBClick}>BUTTONB</button>
      </div>
      <div>BUTTONAが{countA}回押されました</div>
      <div>BUTTONBが{countB}回押されました</div>
    </div>
  );
}
export default App;

このコードの場合、countAが変化することが実行条件となる。

まず、このコンポーネントが表示された時点でコンソールに”mount”と表示される。以降BUTTONAをクリックした場合は再レンダリング時にcountAが変化しているため”unmount”->”mount”の順に表示されるが、BUTTONBをクリックしてもcountAは変化しないためコンソールには何も表示されない。

コメント

タイトルとURLをコピーしました