すべての依存関係が変更された場合のuseEffect? 質問する

すべての依存関係が変更された場合のuseEffect? 質問する

現在はuseEffect依存関係の 1 つだけが変更されたときに起動されます。

すべての依存関係が変更されたときに、それを更新したり起動したりするにはどうすればよいでしょうか?

ベストアンサー1

すべての依存関係が変更されたときにエフェクトを呼び出すためのロジックを追加する必要があります。これでuseEffectAllDepsChange目的の動作が実現されるはずです。

ここでの戦略は、以前の依存関係を現在の依存関係と比較することです。すべてが異なっていない場合は、以前の依存関係を参照に保持し、すべてが異なるまで更新しません。これにより、エフェクトが呼び出される前に依存関係を複数回変更できます。

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

// taken from https://usehooks.com/usePrevious/
function usePrevious(value) {
  const ref = useRef();

  useEffect(() => {
    ref.current = value;
  }, [value]);
  
  return ref.current;
}

function useEffectAllDepsChange(fn, deps) {
  const prevDeps = usePrevious(deps);
  const changeTarget = useRef();

  useEffect(() => {
    // nothing to compare to yet
    if (changeTarget.current === undefined) {
      changeTarget.current = prevDeps;
    }

    // we're mounting, so call the callback
    if (changeTarget.current === undefined) {
      return fn();
    }

    // make sure every dependency has changed
    if (changeTarget.current.every((dep, i) => dep !== deps[i])) {
      changeTarget.current = deps;

      return fn();
    }
  }, [fn, prevDeps, deps]);
}

export default function App() {
  const [a, setA] = useState(0);
  const [b, setB] = useState(0);

  useEffectAllDepsChange(() => {
    console.log("running effect", [a, b]);
  }, [a, b]);

  return (
    <div>
      <button onClick={() => setA((prev) => prev + 1)}>A: {a}</button>
      <button onClick={() => setB((prev) => prev + 1)}>B: {b}</button>
    </div>
  );
}

編集 vital-sky-q9hju

Richard にヒントを得た別のアプローチはよりクリーンですが、更新ごとにレンダリングが増えるという欠点があります。

function useEffectAllDepsChange(fn, deps) {
  const [changeTarget, setChangeTarget] = useState(deps);

  useEffect(() => {
    setChangeTarget(prev => {
      if (prev.every((dep, i) => dep !== deps[i])) {
        return deps;
      }

      return prev;
    });
  }, [deps]);

  useEffect(fn, changeTarget);
}

おすすめ記事