当依赖关系是一个函数时,如何避免useEffect中的中篇更新



当页面呈现时,挂钩正在检查存储中是否存在模拟信息,如果存在,则在全局AppState上下文中设置这些信息。

const impersonateStorageKey = `impersonation_${config.environment}`
// signature const impersonate: (empScope: number) => void
const { currentImpersonation, impersonate } = useAppContext()
useEffect(() => {
if (!window || !window.localStorage) return;
const storageString = localStorage.getItem(impersonateStorageKey)
if (!storageString) return;
const data = JSON.parse(storageString)
impersonate(data.currentImpersonation)
}, [impersonateStorageKey])

使用第二个钩子,将当前模拟身份的更改持久化到存储中:

useEffect(() => {
if (!window || !window.localStorage) return;
localStorage.setItem(impersonateStorageKey, JSON.stringify({ /* ... */}))
}, [currentImpersonation, impersonateStorageKey])

加上useAppContext的相关位

const useAppContext = () => {
const { state, dispatch } = useContext(AppContext)
if (!state) {
throw new Error("useAppContext must be used within within  AppContextProvider")
}

const impersonate = (employeeScope: string | number) => dispatch({ type: 'IMPERSONATE', value: employeeScope })
const currentImpersonation = state.currentImpersonation
return {
impersonate,
currentImpersonation,
}
}

这是正常的,但linter抱怨说,第一个useEffect钩子中缺少依赖模拟

当我将impersonate添加到依赖数组时,这将导致一个持续的更新循环,并使应用程序没有响应。

我知道是什么导致了这种行为,但我没有找到一个解决方案(除了忽略规则(来解决如何打破循环让linter高兴。

我可以采取什么方法?

使用useCallback:创建函数时,可以对其进行记忆

const useAppContext = () => {
const { state, dispatch } = useContext(AppContext)
const impersonate = useCallback(
(employeeScope: string | number) => dispatch({
type: 'IMPERSONATE',
value: employeeScope
}), [dispatch])
if (!state) {
throw new Error("useAppContext must be used within within  AppContextProvider")
}
const currentImpersonation = state.currentImpersonation
return {
impersonate,
currentImpersonation,
}
}

如果不能,解决方法是将函数放在引用中。ref是对对象的不可变引用,具有可变的current属性。由于ref本身是不可变的,您可以将其用作依赖项,而无需激活useEffect(linter知道它,您甚至不需要将其声明为依赖项(。你可以随心所欲地变异current

const impersonateRef = useRef(impersonate)
useEffect(() => {
impersonateRef.current = impersonate
}, [impersonate])
useEffect(() => {
if (!window || !window.localStorage) return
const storageString = localStorage.getItem(impersonateStorageKey)
if (!storageString) return
const data = JSON.parse(storageString)
impersonateRef.current(data.currentImpersonation)
}, [impersonateStorageKey])

最新更新