确定哪个依赖关系数组变量导致使用效果挂钩触发这个库...@simbathesailor/使用更改的内容,就像一个魅力!

2022-08-30 02:21:59

有没有一种简单的方法来确定 的依赖数组中的哪个变量触发函数重新触发?useEffect

简单地注销每个变量可能会产生误导,如果是一个函数并且是一个对象,它们在记录时可能会看起来相同,但实际上是不同的,并导致useEffect触发。ab

例如:

React.useEffect(() => {
  // which variable triggered this re-fire?
  console.log('---useEffect---')
}, [a, b, c, d])

我目前的方法一直在逐个删除依赖项变量,直到我注意到导致过度useEffect调用的行为,但必须有更好的方法来缩小范围。


答案 1

我最终从各种答案中吸取了一点点,为此制作了自己的钩子。我希望能够删除一些东西来代替,以便快速调试触发的依赖关系。useEffectuseEffect

const usePrevious = (value, initialValue) => {
  const ref = useRef(initialValue);
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};
const useEffectDebugger = (effectHook, dependencies, dependencyNames = []) => {
  const previousDeps = usePrevious(dependencies, []);

  const changedDeps = dependencies.reduce((accum, dependency, index) => {
    if (dependency !== previousDeps[index]) {
      const keyName = dependencyNames[index] || index;
      return {
        ...accum,
        [keyName]: {
          before: previousDeps[index],
          after: dependency
        }
      };
    }

    return accum;
  }, {});

  if (Object.keys(changedDeps).length) {
    console.log('[use-effect-debugger] ', changedDeps);
  }

  useEffect(effectHook, dependencies);
};

下面是两个示例。对于每个示例,我假设从“foo”更改为“bar”。示例 1 显示了不通过的输出,示例 2 显示了一个带有 .dep2dependencyNamesdependencyNames

示例 1

以前:

useEffect(() => {
  // useEffect code here... 
}, [dep1, dep2])

后:

useEffectDebugger(() => {
  // useEffect code here... 
}, [dep1, dep2])

控制台输出:

{
  1: {
    before: 'foo',
    after: 'bar'
  }
}

对象键“1”表示已更改的依赖项的索引。此处已更改,因为它是依赖项中的第 2 项或索引 1。dep2

示例 2

以前:

useEffect(() => {
  // useEffect code here... 
}, [dep1, dep2])

后:

useEffectDebugger(() => {
  // useEffect code here... 
}, [dep1, dep2], ['dep1', 'dep2'])

控制台输出:

{
  dep2: {
    before: 'foo',
    after: 'bar'
  }
}

答案 2

这个库...@simbathesailor/使用更改的内容,就像一个魅力!

  1. 使用 和 或 安装npm/yarn--dev--no-save
  2. 添加导入:
import { useWhatChanged } from '@simbathesailor/use-what-changed';
  1. 称呼它:
// (guarantee useEffect deps are in sync with useWhatChanged)
let deps = [a, b, c, d]

useWhatChanged(deps, 'a, b, c, d');
useEffect(() => {
  // your effect
}, deps);

在控制台中创建这个漂亮的图表:

image loaded from github

有两个常见的罪魁祸首:

  1. 一些对象被传入,如下所示:
// Being used like:
export function App() {
  return <MyComponent fetchOptions={{
    urlThing: '/foo',
    headerThing: 'FOO-BAR'
  })
}
export const MyComponent = ({fetchOptions}) => {
  const [someData, setSomeData] = useState()
  useEffect(() => {
    window.fetch(fetchOptions).then((data) => {
      setSomeData(data)
    })

  }, [fetchOptions])

  return <div>hello {someData.firstName}</div>
}

对象情况下的修复,如果可以的话,在组件渲染之外分解静态对象:

const fetchSomeDataOptions = {
  urlThing: '/foo',
  headerThing: 'FOO-BAR'
}
export function App() {
  return <MyComponent fetchOptions={fetchSomeDataOptions} />
}

您还可以在 useMemo 中包装:

export function App() {
  return <MyComponent fetchOptions={
    useMemo(
      () => {
        return {
          urlThing: '/foo',
          headerThing: 'FOO-BAR',
          variableThing: hash(someTimestamp)
        }
      },
      [hash, someTimestamp]
    )
  } />
}

同样的概念在一定程度上也适用于函数,除了你最终可能会得到过时的闭包。