• memo、useCallback、useMemo的区别和用法


    memo、useCallback、useMemo的区别和用法

    react在渲染父子嵌套组件的时候,有时会发生不必要的渲染,根据经验总结出来,大致有四种情况需要处理:

    1. 父子组件嵌套,父组件未向子组件传值
    2. 父子组件嵌套,父组件向子组件传值,值类型为值类型
    3. 父子组件嵌套,父组件向子组件传值,值得类型为方法
    4. 父子组件嵌套,父组件向子组件传值,值得类型为对象

    首先看第一种情况,看如下代码:

    子组件:

    function ChildComp () {
      console.log('render child-comp ...')
      return <div>Child Comp ...</div>
    }

    父组件:

    function ParentComp () {
      const [ count, setCount ] = useState(0)
      const increment = () => setCount(count + 1)
    
      return (
        <div>
          <button onClick={increment}>点击次数:{count}</button>
          <ChildComp />
        </div>
      );
    }

    代码解读:当点击父组件中的button按钮时,父组件中的count发生变化,父组件会重新渲染,但是此时子组件也会重新渲染,这是不必要的,该怎么解决呢?我们此时可以用memo来解决,memo函数的第一个参数是组件,结果返回一个新的组件,这个组件会对组件的参数进行浅对比,当组件的参数发生变化组件才会重新渲染,而上面的实例子组件根本没有传递参数,所以不会随着父组件渲染。

    第二种情况,当父组件给子组件传值,当父组件传递的值是值类型,完全可以用memo来解决。

    第三种情况当父组件给子组件传值,当父组件传递的值是方法函数,看代码:

    子组件:

    import React, { memo } from 'react'
    
    const ChildComp = memo(function ({ name, onClick }) {
      console.log('render child-comp ...')
      return <>
        <div>Child Comp ... {name}</div>
        <button onClick={() => onClick('hello')}>改变 name 值</button>
      </>
    })

    父组件:

    function ParentComp () {
      const [ count, setCount ] = useState(0)
      const increment = () => setCount(count + 1)
    
      const [ name, setName ] = useState('hi~')
      const changeName = (newName) => setName(newName)  // 父组件渲染时会创建一个新的函数
    
      return (
        <div>
          <button onClick={increment}>点击次数:{count}</button>
          <ChildComp name={name} onClick={changeName}/>
        </div>
      );
    }

    父组件在调用子组件时传递了 name 属性和 onClick 属性,此时点击父组件的按钮,可以看到控制台中打印出子组件被渲染的信息。

    我们看到meomo失效了,为什么因为memo进行的是浅对比,父组件重新渲染,changename等于重新生成了一次,所以子组件的props发生了变化,所以子组件也会跟着重新渲染,该怎么应对呢?这时就需要用到useCallback,useCallback

    是一个函数,其参数是需要被缓存的方法,我们观察上面代码,发现changename方法需要被缓存,所用useCallback将其缓存一下,如何使用呢,useCallback类似函数装饰器,参数函数,结果返回一个新函数,看代码:

    import React, { useCallback } from 'react'
    
    function ParentComp () {
      // ...
      const [ name, setName ] = useState('hi~')
      // 每次父组件渲染,返回的是同一个函数引用
      const changeName = useCallback((newName) => setName(newName), [])  
    
      return (
        <div>
          <button onClick={increment}>点击次数:{count}</button>
          <ChildComp name={name} onClick={changeName}/>
        </div>
      );
    }

    此时点击父组件按钮,控制台不会打印子组件被渲染的信息了。

    究其原因:useCallback() 起到了缓存的作用,即便父组件渲染了,useCallback() 包裹的函数也不会重新生成,会返回上一次的函数引用。

    第四种情况父子组件嵌套,父组件向子组件传值,值得类型为对象,前面父组件调用子组件时传递的 name 属性是个字符串,如果换成传递对象会怎样? 下面例子中,父组件在调用子组件时传递 info 属性,info 的值是个对象字面量,点击父组件按钮时,发现控制台打印出子组件被渲染的信息。 代码如下:

    import React, { useCallback } from 'react'
    
    function ParentComp () {
      // ...
      const [ name, setName ] = useState('hi~')
      const [ age, setAge ] = useState(20)
      const changeName = useCallback((newName) => setName(newName), [])
      const info = { name, age }    // 复杂数据类型属性
    
      return (
        <div>
          <button onClick={increment}>点击次数:{count}</button>
          <ChildComp info={info} onClick={changeName}/>
        </div>
      );
    }

    分析原因跟调用函数是一样的:

    1. 点击父组件按钮,触发父组件重新渲染;
    2. 父组件渲染,const info = { name, age } 一行会重新生成一个新对象,导致传递给子组件的 info 属性值变化,进而导致子组件重新渲染。

    使用 useMemo 对对象属性包一层。 useMemo 有两个参数: 第一个参数是个函数,返回的对象指向同一个引用,不会创建新对象; 第二个参数是个数组,只有数组中的变量改变时,第一个参数的函数才会返回一个新的对象。看代码:

    function ParentComp () {
      // ....
      const [ name, setName ] = useState('hi~')
      const [ age, setAge ] = useState(20)
      const changeName = useCallback((newName) => setName(newName), [])
      const info = useMemo(() => ({ name, age }), [name, age])   // 包一层
    
      return (
        <div>
          <button onClick={increment}>点击次数:{count}</button>
          <ChildComp info={info} onClick={changeName}/>
        </div>
      );
    }

    再次点击父组件按钮,控制台中不再打印子组件被渲染的信息了。

    以上便是memo、useCallback、useMemo的区别和用法,希望对你有所帮助。

    原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

    如有侵权,请联系 cloudcommunity@tencent.com 删除。

     
  • 相关阅读:
    第四讲:工厂模式在开发中的运用
    第一讲:简单工厂模式
    第二讲:工厂方法模式
    第三讲:抽象工厂模式
    第三十一讲:UML类图(上)
    第三十讲:基础五迪米特法则
    第二十九讲:基础四依赖倒转原则
    第二十八讲:基础三里氏代换原则
    第二十七讲:基础二单一职责原则
    第二十六讲:基础一开放封闭原则
  • 原文地址:https://www.cnblogs.com/sexintercourse/p/16778966.html
Copyright © 2020-2023  润新知