【1024用代码改变世界】useMemo 和 useCallback|React.memo使用场景

这篇具有很好参考价值的文章主要介绍了【1024用代码改变世界】useMemo 和 useCallback|React.memo使用场景。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

欢迎来到我的博客
📔博主是一名大学在读本科生,主要学习方向是前端。
🍭目前已经更新了【Vue】、【React–从基础到实战】、【TypeScript】等等系列专栏
🛠目前正在学习的是🔥 R e a c t / 小程序 React/小程序 React/小程序🔥,中间穿插了一些基础知识的回顾
🌈博客主页👉codeMak1r.小新的博客

本文被专栏【React–从基础到实战】收录

const testcomponent = react.memo,前端面试,前端,javascript,react.js,前端性能优化

🕹坚持创作✏️,一起学习📖,码出未来👨🏻‍💻!

前言

useMemo / useCallback都是React内置的用于性能优化的hook,它们常常被开发人员用来包裹(缓存memory),但是真的是所有的数据、函数、变量都需要使用useMemo / useCallback去缓存吗?
可直接看结论。

useMemo / useCallback都是用以性能优化的hook,开发者经常担心两次渲染间的重复计算,而去过度使用useMemo / useCallback,担心性能问题的开发者们,给几乎每个变量都套上了useMemo,给每个函数都套上了useCallback……其实这是不可取的,这让代码看起来像是必须使用这两个hook去优化一样,无处不在。

本文希望通过分析 useMemo/useCallback 的目的、方式、成本,以及具体使用场景,帮助开发者正确的决定如何适时的使用他们。赶时间的读者可以直接拉到底部看结论。

我们先从 useMemo/useCallback 的目的说起。

何时应该使用useMemo / useCallback ?

防止不必要的 effect

小新在编码的过程中,如果effect有依赖的变量,我就会把effect里的内容提到effect外面,包装成一个函数,再用useCallback去缓存这个函数,那么只要这个变量不变化,effect依赖的这个函数也不会改变(不使用useCallback缓存的话,此函数的内存地址可能会发生变化,哪怕其内部不改变)。

const Component = () => {
  const a = React.useMemo(() => ({ test: 1 }), [])
  React.useEffect(() => {
    // dosomthing
  }, [a])
  return (
    <div>{a.test}</div>
  )
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Component />);

只有a的值改变时,dosomthing才会重新触发,而a被useMemo缓存了,这就导致非必要时,effect不会重新创建,这是好的优化;

useCallback也是一样的,(useCallback其实是useMemo的语法糖)

const Component = () => {
  const ajax = React.useCallback(() => {
    console.log('^ajax somthing^!')
  }, [])
  React.useEffect(() => {
    // dosomthing
    ajax()
  }, [ajax])
  return (
    <div></div>
  )
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Component />);

此代码段中的Component组件,只有当ajax函数变化时才会重新创建一个effect,这就导致,我们可以把仅需要在页面首次加载时发送的ajax请求封装成一个函数,并且用useCallback优化缓存下来,这是好的优化;

防止不必要的re-render

我们首先思考,当什么情况出现时,组件才会re-render

  • 当本身的props或state改变时;
  • context上下文的value改变时,使用该值的组件都会重新render;
  • 当父组件重新render时,哪怕传入子组件的props没有发生改变,子组件(们)也会随着父组件的render,重新render;

第三个re-render经常被开发者忽视,其实这一点很重要!!

例如,

const Component = () => {
  const [state, setState] = React.useState(1);

  const onClick = React.useCallback(() => {
    console.log('^click somthing^!')
  }, []);

  return (
	// 哪怕onClick使用了useCallback缓存优化,但是自组件仍会re-render
    <Child onClick={onClick} />
  )
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Component />);

哪怕onClick使用了useCallback缓存优化,但是自组件仍会re-render。这里的useCallback似乎是无效的。

那么,怎么让其生效呢?

我们可以搭配React.memo去使用:

const PageMemoized = React.memo(Page);

React.memo本质是一个HOC,它接受一个组件作为参数。被memo包裹的Page组件,会在Page组件的父组件Component重新render时,对比传入Page组件的props( 浅比较,复杂对象只比较第一层),若props没有发生改变,则Pages组件就不会re-render

所以, 必须同时缓存 onClick 和组件本身,才能实现 Page 不触发 re-render。

PageMemoized会在父组件重新render时,浅比较传入的onClick是否变化再决定PageMemoized组件是否需要re-render,但是onClick正好被useCallback缓存了,所以这里的子组件不会re-render(●–●)

但是,如果PageMemoized组件从父组件不止接受了onClick一个prop,那么前面的优化就前功尽弃。比如,

// 省略重复代码

<PageMemoized onClick={onClick} value={[1, 2, 3]} />

每次父组件重新re-render时,传入子组件的onClick函数虽然没有改变(useCallback的功劳),但是value并没有做任何缓存,此时,子组件PageMemoized,还是逃脱不了re-render的命运……

怎么解决呢?

// 省略重复代码
const value = useMemo(() => {
  return [1, 2, 3]
}, [])

// ...
<PageMemoized onClick={onClick} value={[1, 2, 3]} />

这样的话,value变量也被缓存起来了,父组件re-render时,自组件并没有re-render。

由此我们知道, 必须同时缓存 所有的prop 和组件本身,才能实现子组件 不触发 re-render。

如何判断子组件是否需要缓存?

如果所有的子组件都需要缓存,那未免也太麻烦了……不光需要memo子组件,还需要将现有的props都进行缓存,并且还包括了后续编码可能出现的其他props……

除此之外,还有更严重的后果,如果项目中的组件缓存过多的话,可能会导致 项目在首次初始化时因为组件缓存被拖慢渲染时间。

所以,局部的,有选择的去使用memo,比全局都使用memo更加恰当、更加优雅。

至于怎样判断组件的渲染成本,可以借助React Devtool等工具去判断,或者根据开发者经验人工判断。

防止不必要的计算

React官方文档介绍:

useMemo返回一个 memoized 值。
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。

什么才是高开销呢?

借助前端经典面试题提供的测试用例,对包含 250 个 item 的数组 countries 进行排序、渲染,并计算耗时。结果发现,排序耗时仅用了4ms,而渲染这些List却用了20ms,5倍的差距!而日常开发中,大部分情况下都是,计算的数据更少,而渲染的组件更多。这也就说明了, 大部分情况下,真正的性能瓶颈不是计算,而是渲染。 所以应该把useMemo用在渲染昂贵的组件上,而不是计算上。

那为什么不给所有的组件都使用useMemo呢?前面也说了,缓存会影响项目初始化的速度,而且可能会导致与PureComponent相同的问题,传入子组件的prop浅层并无变化,于是被useMemo包裹的子组件并不会re-render,但其实此时正需要它re-render。

结论

  1. 大部分的 useMemo 和 useCallback 都应该移除,他们可能没有带来任何性能上的优化,反而增加了程序首次渲染的负担,并增加程序的复杂性。
  2. 使用 useMemo 和 useCallback 优化子组件 re-render 时,必须同时满足:
    • 子组件被React.memo 或 useMemo 缓存;
    • 子组件所有的prop都被缓存。
  3. 不推荐默认给所有组件都使用缓存,大量组件初始化时被缓存,可能导致过多的内存消耗,并影响程序初始化渲染的速度。

专栏订阅入口【React–从基础到实战】文章来源地址https://www.toymoban.com/news/detail-786263.html

到了这里,关于【1024用代码改变世界】useMemo 和 useCallback|React.memo使用场景的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 理解 React 中的 useEffect、useMemo 与 useCallback

    先理解 useEffect 有助于学习 useMemo 和 useCallback。因为 useMemo 和 useCallback 的实现实际上都是基于 useEffect 的。 useEffect 是 React 中的一个很重要的 Hook,用于执行副作用操作。什么是副作用?简单来说,就是那些会改变函数外部变量或有外部可观察影响的操作。useEffect 允许你在函

    2024年02月03日
    浏览(41)
  • useMemo和useCallback使用场景

    useMemo到底是做什么的,工作原理是什么。 简而言之,useMemo是用来缓存 计算属性 的。 计算属性其实是函数的返回值,或者说指那些以返回一个值为目标的函数。 有些函数,需要我们手动的去点击,去完成一些动作才触发。而有些函数,则是直接在渲染的时候就执行,在DO

    2024年02月15日
    浏览(44)
  • React useMemo 实际开发使用小结

    useMemo 的原理是基于 memoization 技术。当你使用 useMemo 时,它会在组件渲染过程中缓存函数的计算结果,并在下一次渲染时,仅在依赖项(dependencies)发生变化时重新计算。如果依赖项没有发生变化,则直接返回之前缓存的结果,避免不必要的重复计算。 在组件初次渲染时,

    2024年02月16日
    浏览(40)
  • React中useMemo的简单使用

    useMemo主要用来解决使用React hooks产生的无用渲染的性能问题,用来做缓存用。 useMemo使用场景,比如有两个变量(依赖项),只需要在其中一个变量变化时发生变化,否则拿缓存的值;或者其中另一个变量的变化不需要引起重新计算时使用。该属性类似于vue中的计算属性,有

    2024年02月13日
    浏览(42)
  • React.memo、shouldComponentUpdate、PureComponent的基本使用

    当我们组件内部有大量的计算是,避免组件内部进行不必要的重新渲染,使用React.memo进行缓存组件,避免不必要的重新渲染 React.memo 是用来判断是否需要重新渲染组件,和 shouldComponentUpdate 的区别是 shouldComponentUpdate 用于class组件方式,而 React.memo 用于 hooks 方式 语法 React.me

    2024年02月06日
    浏览(33)
  • react useState useEffect useMemo实际业务场景中的使用

    下面的代码实现了上面图片的功能

    2024年02月16日
    浏览(42)
  • React 中的 useCallback 钩子函数

    useCallback 钩子函数有点像 useMemo 一样可以备份信息,而 useCallback 只是备份函数,除非某些参数发生变化,否则他不会重新运行其中的代码, 出现的问题:运行上述代码后,当我们在输入框中输入数字后,再去查看控制台的日志我们可以看到打印出了 params change 的信息,这就

    2024年02月09日
    浏览(40)
  • [React]useMemoizedFn和useCallback对比

    useMemoizedFn文档地址:https://ahooks.js.org/zh-CN/hooks/use-memoized-fn 在 React 中,自定义的 Hooks 内部的函数在以下常见的几种情况下会被重新赋值,导致更新引用: 组件重新渲染: 当组件重新渲染时,Hooks 内部的函数会被重新执行,从而导致函数的重新赋值和更新引用。 这意味着每

    2024年02月14日
    浏览(42)
  • 5.React.memo 性能优化

    性能优化, React.memo 2. React.memo类似纯组件,可提高组件性能表现(类组件PureComponent)

    2024年02月11日
    浏览(35)
  • React Hook之useCallback 性能优化

    上文 对比之前的组件优化说明React.memo的作用我们说了 React.memo的妙用 但是 它却并非万能 我们来看这个情况 我们子组件代码编写如下 这里 我们接收了父组件 props中的一个 dom1funt 函数 然后点击dom1funt按钮 触发这个dom1funt 然后 父组件代码编写如下 父组件 我们定义了这个传给

    2024年02月11日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包