react17: memo、useMemo和useCallback使用总结

        React 所做的主要事情是让我们的 UI 与我们的 状态 保持同步,而要实现它们的同步,就需要执行一个叫做 “re-render” (重新渲染) 的操作。 从本质上,useMemo 和 useCallback 都是用来帮助我们优化 重新渲染 的工具 Hook。它们通过以下两种方式实现优化的效果。

  • 减少在一次渲染中需要完成的工作量。

  • 减少一个组件需要重新渲染的次数。

  • memo:使用 React.memo 包裹着组件,告诉react这是纯组件,用于保护它不会受到无关状态更新的影响,即:只会在收到新数据(props)或内部状态发生变化时重新渲染。
  • useMemo: (1)需要进行大量计算的场景:缓存计算结果 (类似vue中的计算属性);(2)引用保留:保留某个复杂类型的引用(如:特定数组/对象变量等复杂类型,非基本数据类型)。
  • useCallback:(1)引用保留:保留某个函数的引用。

        注意:useMemo 和 useCallback 是一个东西,只是将返回值从 数组/对象 替换为了 函数。useCallback 是一种语法糖,它的存在存粹是为了让我们在缓存回调函数的时候可以方便点。

        前言:在一个父组件中,每次父组件中的状态更改时(使用setState修改),都会触发父组件的重新渲染,从而也导致所有子组件重新渲染。

一、memo

语法:memo(函数组件,(prevProps, props)=> { ...  return 布尔值 }),第2个参数为前后props的值的比较方式,判断props是否改变,默认是浅比较,返回true时,不会触发render,返回false时则会触发render。

用于解决的问题:(1)子组件没有任何props, 但是父组件每次渲染也会导致该子组件重新渲染。(2)子组件有基本数据类型props,props值都未更改,父组件渲染也会导致子组件重新渲染。

import { memo } from 'react';
const PureComponentChild = () => {
  return (
    ...
  )
}
// 使用memo包裹:只有props或自身的状态更改时,组件才会渲染。
export default memo(PureComponentChild)

小扩展:父组件中:(1)引用类型props变量:使用useState声明(memo生效)、 在组件内使用普通变量声明(memo不生效,问题原因:每次渲染重新生成新的引用问题)、在函数组件外声明的普通引用变量(memo生效)。(2)基本数据类型props变量:以上3种写法,memo都生效(即子组件都会缓存,不会重复渲染)(3)在某个函数内,调用setState2次修改父组件中的2个变量值,最后父组件也只渲染一次。

二、useMemo

语法: useMemo(() => { return 复杂类型变量值或函数 }, [依赖变量数组] ),返回值:任意变量

用于解决的问题:(1)子组件使用memo包裹了, 但是接收的props是"未改变"的(引用类型props,如:对象、数组和函数等变量),此时父组件渲染还是会导致该组件重新渲染。产生问题原理:复杂类型,每次父组件渲染,会生成新的引用导致。(2)某个进行大量计算的逻辑处理的计算结果,每次渲染,都会重新计算结果值,耗费性能。

子组件: 

import { useMemo, memo } from 'react'
// props: 接收一个引用类型 list
const ChildA = ({ list }) => {
   return (
      list.map((item) => {
        return <h1 key={item.name}>{item.name}</h1>;
      });
  )
}
export default memo(ChildA)

父组件:

import ChildA from './components/ChildA';
import { useMemo, useState } from 'react';
import { Button } from 'antd'

// 父组件
const ParentCom = () => {
  const [name, setName] = useState('这是父组件默认名');
  // 写法1:直接在父组件中声明复杂类型变量,父组件渲染 -》子组件也会重新渲染。
  // const list = [{ name: '小明' }, { name: '小花' }];
  // 写法2:使用useMemo包裹,依赖数组为空,父组件渲染 -》 子组件不会重新渲染(除非依赖数组中变量的值改变)。
  const list = useMemo(() => {
    return [{ name: '小明' }, { name: '小花' }];
  }, []);

  const modifyName = () => {
    setName('新名' + Date.now());
  };

  return (
    <>
      <h1>父组件名: {name}</h1>
      <Button onClick={modifyName} />
      <ChildA list={list} />;
    </>
  );
};
export default ParentCom;

三、useCallback

语法:useCallback(() => { 函数内部的处理逻辑 }, [依赖变量数组] ),返回值:是一个函数(引用)。

用于解决的问题:(1)子组件使用memo包裹,props有传函数,每次父组件更新,即使函数未改变,也会重新渲染问题。

import { useCallback, useMemo } from 'react'

// useCallback方式
const modifyParentNameFun = useCallback(() => {
   ... // 函数内部自定义处理逻辑
},[])

// useMemo方式:跟使用useCallback是等效的,一样的功能!2者选一即可!
const modifyParentNameFun = useMemo(() => {
   return function() [
    ... // 函数内部自定义处理逻辑
   }
},[])

<ChildA modifyParentNameFun={modifyParentNameFun}>

参考网址:

猜你喜欢

转载自blog.csdn.net/qq_38969618/article/details/130748768