首页 > 其他分享 >memo、useCallback、useMemo的区别和用法

memo、useCallback、useMemo的区别和用法

时间:2022-10-11 14:02:12浏览次数:52  
标签:count const name 渲染 useMemo memo useCallback 组件

memo、useCallback、useMemo的区别和用法

发布于2021-12-06 11:26:26阅读 3980  

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的区别和用法,希望对你有所帮助。

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

如有侵权,请联系 [email protected] 删除。

   

标签:count,const,name,渲染,useMemo,memo,useCallback,组件
From: https://www.cnblogs.com/sexintercourse/p/16778966.html

相关文章