加载Gatsby页面后,我正尝试更新上下文。
我这样做的方式是,将上下文提供给所有页面,并在页面加载后更新上下文(使用useEffect
完成以确保仅在组件安装时发生)。
不幸的是,这会导致无限的渲染循环(也许不是在Firefox中,而是至少在Chrome中)。
为什么会这样?我的意思是,上下文更新意味着将重新渲染提供程序下面的所有组件,但是useEffect应该只运行一次,那就是在组件安装时。
这是代码:https://codesandbox.io/s/6l3337447n
当您转到第二页(位于第一页底部的链接)时,就会发生无限循环。
如果我想在每次页面加载时更新上下文,这里的解决方案是什么?
答案 0 :(得分:1)
默认情况下,useEffect
运行每个渲染。在您的示例中,useEffect
每次渲染都会更新context
,从而触发无限循环。
如果要运行效果并仅将其清理一次(在挂载和卸载时),则可以将空数组([])作为第二个参数传递。这告诉React,您的效果不依赖于道具或状态的任何值,因此它不需要重新运行。这不是特殊情况,它直接取决于依赖项数组始终如何工作。
因此适用于您的示例:
useEffect(() => {
console.log("CONTEXT DATA WHEN PAGE 2 LOADS:", data)
mergeData({
location,
})
- }, [location, mergeData, data])
+ }, [])
这样,useEffect
仅在首次安装时运行。我认为您也可以将location
留在其中,因为useEffect
并不依赖于context
的值,因此也可以防止无限循环。
答案 1 :(得分:1)
此问题的正确答案不是将空的依赖项数组传递给useEffect
,而是将上下文的mergeData
包装在useCallback
hook中。我无法编辑您的代码,但您可能还需要像下面的示例一样向您的useCallback
添加依赖项
import React, { useState, useCallback } from "react"
const defaultContextValue = {
data: {
// set initial data shape here
menuOpen: false,
},
mergeData: () => {},
}
const Context = React.createContext(defaultContextValue)
const { Provider } = Context
function ContextProviderComponent({ children }) {
const [data, setData] = useState({
...defaultContextValue,
mergeData, // shorthand method name
})
const mergeData = useCallback((newData) {
setData(oldData => ({
...oldData,
data: {
...oldData.data,
...newData,
},
}))
}, [setData])
return <Provider value={data}>{children}</Provider>
}
export { Context as default, ContextProviderComponent }
选择的答案是错误的,因为反应文档明确表示不要忽略当前选择的答案所暗示的依赖。
如果您将es-lint
与eslint-plugin-react-hooks一起使用,则会告诉您这是不正确的。
注意
如果使用此优化,请确保数组包含所有值 从组件范围(例如道具和状态)转变过来 时间和效果所使用的时间。否则,您的代码将 参考先前渲染中的陈旧值。进一步了解如何 处理函数以及数组更改过于频繁时的操作。
https://reactjs.org/docs/hooks-effect.html
从依赖关系列表中省略函数是否安全?通常 说不很难记住使用了哪些道具或状态 通过功能以外的效果。这就是为什么通常您想要 在其内部声明一个效果所需的函数。那很容易 查看影响作用的组件范围中的哪些值:
https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies