React.memo、React Hooks 里 useMemo 和 useCallback 的区别及运用

作者: tww844475003 分类: 前端开发 发布时间: 2021-06-27 13:07

useMemo

用来缓存数据,当组件内部某一部分渲染的数据(组件),需要通过计算而来,这个计算是依赖与特定的state、props数据,我们就用 useMemo 来缓存这个数据,以至于我们在修改她们没有依赖的数据源的情况下,多次调用这个计算函数,浪费计算资源。

import React, { useState, useMemo } from 'react';

function UserInfo(props) {
  const [ name, setName ] = useState('');
  const [ userData, setUserData ] = useState({
      name: '伍哥的传说',
      likes: [
        {
          id: 1,
          title: '编程'
        },
        {
          id: 2,
          title: '旅游'
        },
        {
          id: 3,
          title: '跑步'
        },
        {
          id: 4,
          title: '听歌'
        }
      ]
  });

  const getLikes = (data) => {
    console.log('渲染爱好')

    return (
      <>
        {data.map(item => {
          return <li key={item.id}>{item.id}、{item.title}</li>
        })}
      </>
    )
  }

  // BAD 
  // 不使用useMemo的情况下,修改其他属性,也会重新调用 getLikes 方法,浪费计算资源
  // const renderLikes = (data) => {
  //   return getLikes(data)
  // }

  // GOOD
  const renderLikes = useMemo(() => {
    return getLikes(userData.likes)
  }, [userData.likes])

  return (
    <div style={{padding: '20px', border: '1px solid #ccc', borderRadius: '2px', margin: '20px'}}>
      <h1>姓名:{userData.name}</h1>
      <ul>{renderLikes}</ul>
      <input type="text" onChange={(e) => setName(e.target.value)} />
      <input type="submit" onClick={() => setUserData({
        ...userData,
        name 
      })} />
    </div>
  )
}

export default UserInfo;

上面是把组件当做数据缓存起来,其实有更方便的方法只要稍加改动就可以。比如用用 React.memo LikesLi组件

import React, { useState, useMemo } from 'react';

let Likes = function(props) {
  const data = props.data;
  console.log('渲染爱好')

  return (
    <>
      {data.map(item => {
        return <li key={item.id}>{item.id}、{item.title}</li>
      })}
    </>
  )
}

Likes = React.memo(Likes)

function UserInfo(props) {
  const [ name, setName ] = useState('');
  const [ userData, setUserData ] = useState({
      name: '伍哥的传说',
      likes: [
        {
          id: 1,
          title: '编程'
        },
        {
          id: 2,
          title: '旅游'
        },
        {
          id: 3,
          title: '跑步'
        },
        {
          id: 4,
          title: '听歌'
        }
      ]
  });

  const addLikes = () => {
    setUserData({
      ...userData,
      likes: [...userData.likes, {
        id: 5,
        title: '钓鱼'
      }]
    })
  }

  return (
    <div style={{padding: '20px', border: '1px solid #ccc', borderRadius: '2px', margin: '20px'}}>
      <h1>姓名:{userData.name}</h1>
      <ul>
        <Likes data={userData.likes} />
      </ul>
      <input type="text" onChange={(e) => setName(e.target.value)} />
      <input type="submit" onClick={() => setUserData({
        ...userData,
        name 
      })} value="修改姓名" />
      <br /><br />
      <input type="button" value="添加喜好" onClick={addLikes} />
    </div>
  )
}

export default UserInfo;

useCallback

用来缓存函数,这个函数如果是由父组件传递给子组件,或者自定义hooks里面的函数【通常自定义hooks里面的函数,不会依赖于引用它的组件里面的数据】,这时候我们可以考虑缓存这个函数,好处:

1,不用每次重新声明新的函数,避免释放内存、分配内存的计算资源浪费
2,子组件不会因为这个函数的变动重新渲染。【和React.memo搭配使用】

由此可见,useCallback 主要用来缓存函数,虽然useMemo也能实现,但是callback写法直观。

缓存函数的时候,闭包变量也会被缓存,所以新手还是不推荐使用,容易出bug。每次render时定义新的函数就可以,成本很小。

缓存函数的好处是,保证这个函数的引用不变。比如说有个组件A,是Pure Component,或者被React.memo包裹,而它需要通过props传递一个函数。<A func={func} />。如果每次render,没有用useCallback缓存函数,func都会是一个新的对象,那么Pure Component或者React.memo就没用了。这个时候useCallback作用就体现出来了,能在多次渲染期间保持引用不变(只要dependencies不变)。

UseCallBack的应用场景

1、将回调函数作为props传递给子组件,可以使用UseCallBack来避免子组件不必要的重新渲染;

2、在使用useEffect时,如果依赖项数组中包含回调函数,可以使用UseCallBack来避免不必要的副作用执行;

3、在使用自定义Hook时,可以使用UseCallBack来优化自定义Hook的回调函数;

前端开发那点事
微信公众号搜索“前端开发那点事”

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注