Javascript 确定引发useEffect挂钩的依赖项数组变量

Javascript 确定引发useEffect挂钩的依赖项数组变量,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,是否有一种简单的方法来确定useffect的依赖项数组中的哪个变量触发函数重新激发 如果a是一个函数,而b是一个对象,那么简单地注销每个变量可能会产生误导,它们在记录时可能看起来相同,但实际上不同,并导致useEffect火灾 例如: React.useEffect(() => { // which variable triggered this re-fire? console.log('---useEffect---') }, [a, b, c, d]) 我当前的方法是逐个删

是否有一种简单的方法来确定
useffect
的依赖项数组中的哪个变量触发函数重新激发

如果
a
是一个函数,而
b
是一个对象,那么简单地注销每个变量可能会产生误导,它们在记录时可能看起来相同,但实际上不同,并导致useEffect火灾

例如:

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

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

更新

在经过一点实际使用后,到目前为止,我喜欢以下借鉴了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 });
这允许日志记录知道变量的名称,而无需为此目的使用任何单独的参数


据我所知,没有现成的简单方法可以做到这一点,但您可以插入一个自定义挂钩,它可以跟踪其依赖项并记录更改的内容:

// 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')

您可以这样使用:

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"

还有另一个堆栈溢出线程声明可以使用useRef查看以前的值


最后,我从各种答案中提取了一点,为这个问题制作了自己的钩子。我希望能够删除一些东西来代替
useffect
,以便快速调试触发
useffect
的依赖项

const usePrevious = (value, initialValue) => {
  const ref = useRef(initialValue);
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};
下面是两个例子。对于每个示例,我假设
dep2
从'foo'更改为'bar'。示例1显示了不传递
dependencyNames
的输出,示例2显示了带有
dependencyNames
的示例

示例1

之前:

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

useEffectDebugger(() => {
  // useEffect code here... 
}, [dep1, dep2])
useEffectDebugger(() => {
  // useEffect code here... 
}, [dep1, dep2], ['dep1', 'dep2'])
控制台输出:

{
  1: {
    before: 'foo',
    after: 'bar'
  }
}
{
  dep2: {
    before: 'foo',
    after: 'bar'
  }
}
对象键“1”表示已更改的依赖项的索引。此处,
dep1
已更改,是依赖项或索引1中的第二项

示例2

之前:

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

useEffectDebugger(() => {
  // useEffect code here... 
}, [dep1, dep2])
useEffectDebugger(() => {
  // useEffect code here... 
}, [dep1, dep2], ['dep1', 'dep2'])
控制台输出:

{
  1: {
    before: 'foo',
    after: 'bar'
  }
}
{
  dep2: {
    before: 'foo',
    after: 'bar'
  }
}
这个图书馆
,就像一个符咒!
  • 使用
    npm/warn
    --dev
    --no save
  • 添加导入:
  • 叫它:
  • 在控制台中创建此漂亮的图表:

    有两个常见的罪魁祸首:

  • 正在传入的某些对象如下所示:
  • //像这样使用:
    导出函数App(){
    返回{
    const[someData,setSomeData]=useState()
    useffect(()=>{
    window.fetch(fetchOptions).then((数据)=>{
    setSomeData(数据)
    })
    },[fetchOptions])
    返回hello{someData.firstName}
    }
    
    如果可以,对象情况下的修复将在组件渲染外部打断静态对象:

    const fetchSomeDataOptions = {
      urlThing: '/foo',
      headerThing: 'FOO-BAR'
    }
    export function App() {
      return <MyComponent fetchOptions={fetchSomeDataOptions} />
    }
    
    const fetchSomeDataOptions={
    urlshing:“/foo”,
    标题:“FOO-BAR”
    }
    导出函数App(){
    返回
    }
    
    您还可以在UseMoom中换行:

    export function App() {
      return <MyComponent fetchOptions={
        useMemo(
          () => {
            return {
              urlThing: '/foo',
              headerThing: 'FOO-BAR',
              variableThing: hash(someTimestamp)
            }
          },
          [hash, someTimestamp]
        )
      } />
    }
    
    导出函数App(){
    返回{
    返回{
    urlshing:“/foo”,
    标题:“FOO-BAR”,
    变量:散列(someTimestamp)
    }
    },
    [哈希,时间戳]
    )
    } />
    }
    

    同样的概念在某种程度上也适用于函数,但最终可能会导致过时的闭包。

    我也喜欢这个答案。与我的答案相比,设置它需要做更多的工作,但会提供更好的输出,因为每个依赖项都有一个名称,而我的只说明更改了哪个索引。您可以从包含
    true
    false
    的引用切换到包含
    null
    {prevValue:value}的引用
    如果您想在旧值和新值发生变化时记录它们。发布到其他StackOverflow线程的链接可能会很有用。您应该将此发布到NPM!这太棒了。(点表示一个值没有改变。绿色的复选框表示它确实改变了。)甚至还有一个巴贝尔插件(真的去启动这个家伙的项目!)idk为什么,但它没有记录任何东西me@JamilAlisgenderov我认为useWhatChanged必须使用console.table。。因此,如果您试图在不支持console.table的旧浏览器中进行测试,则可以检查是否定义了console.table。您还可以验证正常的console.log('something changed'、'table defined?'、!!console.table);在useEffect钩子日志中。否则。。。可能在github上用您的react版本提交一个问题+browser@JamilAlisgenderov有没有弄清楚是什么原因导致使用什么更改为不为您记录任何内容?