前言
去掉所有的 useCallback 和 useMemo。 90% 的时间你不需要它。
useCallback
useCallback 是 React 钩子,可以创建对 JavaScript 的引用,这有助于 React 的渲染引擎了解对象没有改变。
例如:
function FidgetSpinner() {
const [spinning, setSpinning] = useState(false)
const newFuncEveryTime = () => {
setSpinning(!spinning)
}
const stableFunc = useCallback(() => {
setSpinning(!spinning)
}, [spinning])
return (
<>
<p>Is it spinning? {spinning}</p>
<Spinner spinning={spinning} onClick={...} />
</>
)
}
<Spinner> 呈现一个旋转或不旋转。 onClick 属性接受更新 spinning 状态的函数。
引用稳定性和重新渲染
React 使用 prop 值来决定何时重新渲染 <Spinner> 组件。当值改变时,组件重新渲染。
对于函数和其他对象,“值”是它们的内存地址。即使函数或对象看起来相同,但拥有了新地址,React 也会认为它不同并重新渲染您的组件。
这就是 useCallback 的用武之地。
const newFuncEveryTime = () => {
setSpinning(!spinning)
}
任何时候 React 的接触 <FidgetSpinner> 组件(调用该函数)时,这都是一个新函数。无论是否更新 DOM,调用组件都会使用新的内存地址重新定义此函数。这会导致重新渲染 <Spinner> 。
const stableFunc = useCallback(() => {
setSpinning(!spinning)
}, [spinning])
这将创建一个稳定的内存地址的‘记忆函数’。它仅在依赖项的数组更改时创建新函数。
在这种情况下,只要 spinning 的值发生变化, useCallback 就会创建一个全新的函数。
缺点
错误的依赖数组会造成一个 JavaScript 闭包问题。
如果像这样定义它:
const stableFunc = useCallback(() => {
setSpinning(!spinning)
}, [])
spinning 值被“永远”地嵌入到函数中。调用此函数不会将 spinning 从 false 切换为 true ,它始终会将其设置为 true。(如果初始值为真,则为假。)
这是一个大问题。
为什么不建议使用useCallback
useCallback引入了内存开销。 JavaScript 机制需要保留所有这些记忆函数的堆栈,并将其携带到组件所在的任何地方。做得太多或弄错了,这会导致内存泄漏和陈旧的渲染。这是一个大问题。- 遇到无限循环。当您使用不稳定的回调或对象作为
useEffect依赖项时,就会发生这种情况。每次渲染都重新定义回调,触发效果,导致重新渲染,这......😬 - 还有人会有这样的写法,千万不要这样做,去定义一个组件吧。
function Component() {
const SubComponent = useCallback(() => {
return <div>This is a component damn it!</div>
}, [])
return (
<>
<p>Lorem Ipsum</p>
{SubComponent()}
</>
)
}
上篇文完
谢谢!
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 20 天