确定哪个依赖数组变量导致 useEffect 钩子触发
Posted
技术标签:
【中文标题】确定哪个依赖数组变量导致 useEffect 钩子触发【英文标题】:Determine which dependency array variable caused useEffect hook to fire 【发布时间】:2019-08-06 19:49:39 【问题描述】:有没有一种简单的方法可以确定useEffect
的依赖数组中的哪个变量会触发函数重新触发?
简单地注销每个变量可能会产生误导,如果a
是一个函数而b
是一个对象,它们在记录时可能看起来相同,但实际上却不同并导致 useEffect 触发。
例如:
React.useEffect(() =>
// which variable triggered this re-fire?
console.log('---useEffect---')
, [a, b, c, d])
我当前的方法一直在逐个删除依赖变量,直到我注意到导致过多 useEffect 调用的行为,但必须有更好的方法来缩小范围。
【问题讨论】:
只是想一想,如果您需要验证哪个变量发生了变化,那么拥有多个useEffects
(每个变化的变量一个可能独立变化的变量)不是很有意义。因为很明显您正在尝试将两个用例合二为一?
【参考方案1】:
更新
经过一点实际的使用,我到目前为止喜欢以下解决方案,它借鉴了 Retsam 解决方案的某些方面:
const compareInputs = (inputKeys, oldInputs, newInputs) =>
inputKeys.forEach(key =>
const oldInput = oldInputs[key];
const newInput = newInputs[key];
if (oldInput !== newInput)
console.log("change detected", key, "old:", oldInput, "new:", newInput);
);
;
const useDependenciesDebugger = inputs =>
const oldInputsRef = useRef(inputs);
const inputValuesArray = Object.values(inputs);
const inputKeysArray = Object.keys(inputs);
useMemo(() =>
const oldInputs = oldInputsRef.current;
compareInputs(inputKeysArray, oldInputs, inputs);
oldInputsRef.current = inputs;
, inputValuesArray); // eslint-disable-line react-hooks/exhaustive-deps
;
然后可以通过复制依赖数组文字并将其更改为对象文字来使用它:
useDependenciesDebugger( state1, state2 );
这允许日志记录知道变量的名称,而无需为此目的使用任何单独的参数。
【讨论】:
我也喜欢这个答案。与我的回答相比,设置起来需要位,但会提供更好的输出,因为每个依赖项都有一个名称,而我的只是说明哪个索引发生了变化。 如果您想在旧值更改时记录旧值以及新值,您可以从持有true
和false
的引用切换到持有null
和prevValue: value
的引用。 【参考方案2】:
据我所知,开箱即用并没有真正简单的方法,但您可以放入一个自定义挂钩来跟踪其依赖项并记录更改的日志:
// Same arguments as useEffect, but with an optional string for logging purposes
const useEffectDebugger = (func, inputs, prefix = "useEffect") =>
// Using a ref to hold the inputs from the previous run (or same run for initial run
const oldInputsRef = useRef(inputs);
useEffect(() =>
// Get the old inputs
const oldInputs = oldInputsRef.current;
// Compare the old inputs to the current inputs
compareInputs(oldInputs, inputs, prefix)
// Save the current inputs
oldInputsRef.current = inputs;
// Execute wrapped effect
func()
, inputs);
;
compareInputs
位可能如下所示:
const compareInputs = (oldInputs, newInputs, prefix) =>
// Edge-case: different array lengths
if(oldInputs.length !== newInputs.length)
// Not helpful to compare item by item, so just output the whole array
console.log(`$prefix - Inputs have a different length`, oldInputs, newInputs)
console.log("Old inputs:", oldInputs)
console.log("New inputs:", newInputs)
return;
// Compare individual items
oldInputs.forEach((oldInput, index) =>
const newInput = newInputs[index];
if(oldInput !== newInput)
console.log(`$prefix - The input changed in position $index`);
console.log("Old value:", oldInput)
console.log("New value:", newInput)
)
你可以这样使用:
useEffectDebugger(() =>
// which variable triggered this re-fire?
console.log('---useEffect---')
, [a, b, c, d], 'Effect Name')
你会得到如下输出:
Effect Name - The input changed in position 2
Old value: "Previous value"
New value: "New value"
【讨论】:
【参考方案3】:还有另一个堆栈溢出线程说明您可以使用 useRef 来查看以前的值。
https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
【讨论】:
发布指向其他 *** 线程的链接可能很有用。【参考方案4】:我最终从各种答案中汲取了一点点来制作我自己的钩子。我希望能够只删除一些东西来代替 useEffect
以快速调试触发 useEffect
的依赖项。
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);
;
以下是两个示例。对于每个示例,我假设dep2
从“foo”变为“bar”。示例 1 显示了 没有 传递 dependencyNames
的输出,示例 2 显示了一个示例 dependencyNames
。
示例 1
之前:
useEffect(() =>
// useEffect code here...
, [dep1, dep2])
之后:
useEffectDebugger(() =>
// useEffect code here...
, [dep1, dep2])
控制台输出:
1:
before: 'foo',
after: 'bar'
对象键“1”表示已更改依赖项的索引。这里,dep1
发生了变化,是依赖项中的第二项,或索引 1
示例 2
之前:
useEffect(() =>
// useEffect code here...
, [dep1, dep2])
之后:
useEffectDebugger(() =>
// useEffect code here...
, [dep1, dep2], ['dep1', 'dep2'])
控制台输出:
dep2:
before: 'foo',
after: 'bar'
【讨论】:
你应该把它发布到 NPM! 这太棒了。 在“这里,dep1 发生了变化,是依赖项中的第二项,或索引 1”处有一个小而重要的错字 - 它应该是 dep2我>!【参考方案5】:这个库...
@simbathesailor/use-what-changed
,就像一个魅力!
Install
与 npm/yarn
和 --dev
或 --no-save
添加导入:
import useWhatChanged from '@simbathesailor/use-what-changed';
-
叫它:
// (guarantee useEffect deps are in sync with useWhatChanged)
let deps = [a, b, c, d]
useWhatChanged(deps, 'a, b, c, d');
useEffect(() =>
// your effect
, deps);
在控制台中创建这个漂亮的图表:
有两个常见的罪魁祸首:
-
一些对象像这样传入:
// 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]
)
/>
同样的概念在一定程度上也适用于函数,但最终可能会出现陈旧的闭包。
【讨论】:
(点表示值没有改变。绿色对号表示它确实改变了。)甚至还有一个 babel 插件(认真地去明星这个家伙项目!)github.com/simbathesailor/use-what-changed 知道为什么,但它没有为我记录任何内容 @JamilAlisgenderov 我认为 useWhatChanged 必须使用 console.table.. 因此,如果您尝试在不支持 console.table 的旧浏览器中进行测试,您可以检查是否定义了 console.table。您还可以验证正常的 console.log('something changed', 'table defined?', !!console.table);在你的 useEffect 挂钩日志中。否则......也许用你的反应版本+浏览器在github上提出问题 @JamilAlisgenderov 有没有想过是什么导致 use-what-changed 没有为您记录任何内容? 故事书似乎不支持它以上是关于确定哪个依赖数组变量导致 useEffect 钩子触发的主要内容,如果未能解决你的问题,请参考以下文章
反应 useEffect 警告以放置缺少的依赖项。但是钩子中的依赖值发生了变化
useEffect 在自定义钩子中使用 ref 缺少依赖项警告
排除这个 useEffect 依赖数组变量是不是明智? lint 推荐 3,但我只想依赖 1