如何避免使用中出现无保证的使用回调 deps 的问题效果部门?



我最近开始使用新的 React Hooks API,我觉得它很棒!

但是,我在依赖项区域遇到了一个小困惑。

这是怎么回事?

基本上,我的用例非常简单,可以通过以下伪代码来说明:

import React, { useState, useCallback, useEffect } from 'react'
function Component() {
const [state, setState] = useState()
const doStuff = useCallback(() => {
// Do something 
setState(result)
}, [setState])
useEffect(() => {
// Do stuff ONLY at mount time
doStuff()
}, [])
return <ExpensivePureComponent doStuff={doStuff} />
}

现在,上面的代码工作正常。

但是在我安装了eslint-plugin-react-hooks之后,有一个警告。我必须声明在我的效果中使用的所有依赖项,在这里,doStuff.

好的,让我们修复该代码:

useEffect(() => {
// Do stuff ONLY at mount time
doStuff()
}, [doStuff])

酷,没有更多的警告!

等等,没有警告,但是...也没有错误?

让我们看看文档对useCallback的看法:

useCallback(fn, deps)

相当于 useMemo(() => fn, deps)

然后,关于useMemo

您可以依赖 useMemo 作为性能优化,而不是语义保证。将来,React 可能会选择"忘记"一些以前记住的值,并在下一次渲染时重新计算它们。

所以,基本上,我的doStuff回调,因此我的useEffect,不能再保证只在挂载时运行了?这不是问题吗?

我了解 eslint 插件背后的原理,但在我看来,useCalback/useMemo依赖项数组与useEffect依赖项数组之间存在危险的混淆,还是我错过了什么?

可能是,因为即使是文档也说我的最终代码很好:

如果由于某种原因无法在效果中移动函数,还有几个选项:

● ...

● 作为最后的手段,您可以添加一个函数来影响依赖项,但将其定义包装到 useCallback Hook 中。这可确保它不会在每个渲染上更改,除非它自己的依赖项也更改


me: blah blah hooks bla

SO:你的问题是什么?:)

嗯,你怎么看?代码安全吗?文档说是,但也说不是,因为回调不能保证不会改变......这有点令人困惑。

上面的伪代码有不好的做法吗?当这种模式无法避免时,该怎么办?// eslint-disable-next-line

虽然文档说

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps)

这并不意味着useCallback是使用useMemo实现的,当然也不是。因此,虽然 useMemo 可能会选择再次计算,但除非依赖项数组中的某些内容发生变化,否则 useCallback 不会更新函数。

此外,由于useState返回的二传手不会改变,因此您无需将其传递给useCallback

const doStuff = useCallback(() => {
// Do something 
setState(result)
}, [])

由于doStuff不会改变,因此除了初始挂载之外,useEffect不会再次调用。

但是,在使用useEffectuseCallback时应该记住的一件事是,如果您在 useCallback 更改中的依赖数组,则将重新创建回调,因此 useEffect 将重新运行。防止此类情况的一种方法是使用useReducer挂钩而不是useState,并依靠dispatch来更新状态,因为它在会话中的应用交互过程中永远不会更改。

import React, { useReducer, useEffect } from 'react'
const initialState = [];
const reducer = (state, action) => {
switch(action.type) {
case 'UPDATE_STATE' : {
return action.payload
}
default: return state;
}
}
function Component() {
const [state, dispatch] = useReducer(reducer, initialState);

useEffect(() => {
// Do stuff ONLY at mount time
dispatch({type: 'UPDATE_RESULT', payload: ['xyz']})
}, [])
return <ExpensivePureComponent dispatch={dispatch} />
}

相关内容

  • 没有找到相关文章

最新更新