如何避免 React hooks 闭包陷阱?

sxkk20082年前知识分享127

什么是 React hooks 闭包陷阱?

在 react 中 提供了一些性能优化函数 react.memouseMemouseCallback

const cachedValue = useMemo((fn) => calculateValue, dependencies)

useMemo:memoized 值,只有依赖项变更的时候才会重新计算

const cachedFn = useCallback(fn, dependencies)

useCallback:memoized 函数,只有依赖项变更的时候才会重新更新

const MemoizedComponent = memo(SomeComponent, arePropsEqual?)

memo:缓存组件,当 props 没变化的时候,不会执行 render。arePropsEqual 是一个可选函数,可以自定义对比新旧的 props, 返回 true 就会缓存,返回 false,就不会缓存。

const arePropsEqual=(oldProps: Props, newProps: Props) => boolean

有时候我们使用了这些函数来优化性能,这些函数与外围的 state 形成闭包,导致缓存函数中获取到的 state 不是最新的值,这就是闭包陷阱。

实例演示

比如下面代码,在项目中有一个计时器组件,还有一个 Child 组件, 点击 Child 组件需要返回 App 组件中的最新 state 值;

import React, { useCallback, useEffect, useLayoutEffect } from 'react'
import 'antd/dist/antd.css'
import { Button, ButtonProps } from 'antd'

const Child = ({ onClick }: ButtonProps) => {
  console.log('render')
  return (
    <Button onClick={onClick} type="primary">
      Button
    Button>
  )
}

const App: React.FC = () => {
  const [count, setCount] = React.useState(0)

  useEffect(() => {
    const time = setInterval(() => {
      setCount((count) => count + 1)
    }, 1000)
    return () => {
      clearInterval(time)
    }
  }, [])

  const handleClick = () => {
    console.log(count)
  }

  return (
    <>
      <h2>{count}h2>
      <Child onClick={handleClick} />
    >
  )
}

export default App

这样没什么问题,但是每次渲染的时候 Child 组件都会执行 render

image.png

为了防止 App 组件在更新的时候,不重复渲染(render)子组件,我们使用 React.memo 包裹下 Child 组件, handleClick 也需要使用 useCallback 包裹,这样 Child 组件只会 render 一次。

const Child = React.memo(({ onClick }: ButtonProps) => {
  console.log('render')
  return (
    <Button onClick={onClick} type="primary">
      Button
    Button>
  )
})
const handleClick = useCallback(() => {
    console.log(count);
  }, []);

这样一来 useCallback 和 state 就形成了一个闭包,每次打印的 state 就是初始化的 state。

image.png

为了获得最新的 state 值,必须将 count 参数写进 useCallback 的第二个参数。

const handleClick = useCallback(() => {
  console.log(count)
}, [count])

但这样,又会导致 Child 组件更新。那么有什么好的解决办法呢?既能防止子组件的更新,又可以获取到最新的 state 值呢?

方法:

我们可以使用 useRef 来存一个函数,每次更新的时候设置 ref.current 的值,通过函数来获取最新的 state 值。

const App: React.FC = () => {
  const [count, setCount] = React.useState(0);
  const ref = React.useRef();

  useEffect(() => {
    const time = setInterval(() => {
      setCount((count) => count + 1);
    }, 1000);
    return () => {
      clearInterval(time);
    };
  }, []);

  const fn = () => {
    console.log(count);
  };

  ref.current = fn;

  const handleClick = useCallback(() => {
    ref.current();
  }, []);

  return (
    <>
      

{count}

); };

image.png

codesandbox 演示地址

小结

解决闭包陷阱的方法

  1. 当页面更新不频繁的时候,不使用 useMemouseCallback 缓存函数来优化页面;

  2. 将更新依赖的参数写进 useCallback 的第二个参数

  3. 使用 useRef 来存在一个函数,用一个函数实时获取最新的 state

以上就是本文全部内容,如果对你有帮助,可以随手点个赞,这对我真的很重要,希望这篇文章对大家有所帮助,也可以参考我往期的文章或者在评论区交流你的想法和心得,欢迎一起探索前端。

本文正在参加「金石计划 . 瓜分 6 万现金大奖」

相关文章

如何应对人工智能对未来的不确定性?

如何应对人工智能对未来的不确定性?

  人工智能(AI)是科技领域的一个热门话题,不仅在学术界和商业领域中有重大影响,而且越来越多的人认为AI将改变我们的未来。 随着AI技术的快速发展,人们越来越担心AI对...

AI接口:推动科技创新与社会发展的引擎

AI接口:推动科技创新与社会发展的引擎

  随着人工智能(AI)技术的快速发展,AI接口成为连接人与机器之间的桥梁,推动科技创新与社会发展迈上新的高度。AI接口的出现不仅在各行业中发挥着重要作用,同时也对人们的生活...

删除所有对象中的物体

删除所有对象中的物体截图

将 glb 文件导入 blender

将 glb 文件导入 blender截图

将 glb 文件导入 blender第二步截图

选择您的模型,然后单击 Import glTF 2.0

选择模型导入截图

如何使用 react 和 three.js 在网站渲染自己的3D模型

哈喽,大家好,我是Ai知识分享,今天翻译一篇文章 《How to Use Three.js And React to Render a 3D Model of Your Self》,内容是当下最流行的...

探索未来智能化发展趋势

探索未来智能化发展趋势

   随着科技的飞速发展,人工智能技术(AI)越来越成为时代的焦点。从传统的人机交互到更高级的机器学习和深度学习,AI技术正不断地扩展其应用领域。本文将探讨AI技术的新趋势和...

基于AI技术的家居智能化解决方案——改善生活质量的新时代

基于AI技术的家居智能化解决方案——改善生活质量的新时代

  智能家居已经成为现代社会家庭生活的热门话题。随着人工智能技术的不断发展和应用,越来越多的家居设备开始拥有自主学习和智能化操作的能力。AI家居应用正成为改善生活质量和提供便...

AI智能绘画软件——让艺术创作更加有趣

AI智能绘画软件——让艺术创作更加有趣

  随着科技的不断发展,AI智能绘画软件越来越受到人们的关注。通过AI技术,这些软件能够模拟人类的绘画过程,甚至可以在一定程度上进行自动化画作创作。这种推陈出新的技术给艺术家...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。