详解React.memo、useMemo、useCallback

1:前言

最近开发中使用到了这几个API,这几个API不经常用,刚看到的时候理解也有点模糊,于是网上找了一些讲解并自己动手做了些实验,算是搞明白了。本文主要讲解React.memo、useMemo、useCallback的知识。

 

2:React.memo

React.memo为高阶组件。

如果你的组件在相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。

React.memo 仅检查 props 变更。如果函数组件被 React.memo 包裹,且其实现中拥有 useStateuseReducer 或 useContext 的 Hook,当 context 发生变化时,它仍会重新渲染。

以上是React官方文档对React.memo的解释。通俗点说就是:当父组件更新的时候,子组件也会更新,即使这个子组件不需要更新。例如子组件不接受props时,或者接受的props相同渲染结果相同,这种情况下子组件就不需要重新渲染。例如:

import React, { useState } from 'react';

//父组件
function App() {

  const [a, setA] = useState(0)

  return (
    <div>
      <h1>a: {a}</h1>
      <button onClick={() => setA(a + 1)}>点击a</button>
      <Memo></Memo>
    </div>
  );
}

//子组件
const Memo = () => {
  return (
      <div>
          { console.log('this is memo Component======') }
      </div>
  )
}

export default App;

这个时候App组件的a变化:

可以看到Memo组件重新渲染了。但是这个时候Memo组件仅做展示作用,不需要重新渲染。这个时候怎么办呢?用Memo可以解决:

import React, { useState } from 'react';

//父组件
function App() {

  const [a, setA] = useState(0)

  return (
    <div>
      <h1>a: {a}</h1>
      <button onClick={() => setA(a + 1)}>点击a</button>
      <AfterMemo></AfterMemo>
    </div>
  );
}

//子组件
const Memo = () => {
  return (
      <div>
          { console.log('this is memo Component======') }
      </div>
  )
}

const AfterMemo = React.memo(Memo)



export default App;

结果:

用React.memo包一下即可解决。通过记忆组件渲染结果的方式提高性能。在这种情况下,React将跳过渲染组件的操作并直接复用最近一次渲染的结果。

 

React.memo仅检查props的变更:

import React, { useState } from 'react';

//父组件
function App() {

  const [a, setA] = useState(0)
  const [b, setB] = useState(0)

  return (
    <div>
      <h1>a: {a}</h1>
      <h1>b: {b}</h1>
      <button onClick={() => setA(a + 1)}>点击a</button>
      <button onClick={() => setB(b + 1)}>点击b</button>
      <AfterMemo b={b}></AfterMemo>
    </div>
  );
}

//子组件
const Memo = ({ b }) => {
  return (
      <div>
          { console.log('this is memo Component======', b) }
      </div>
  )
}

const AfterMemo = React.memo(Memo)



export default App;

结果:

当b变化时,子组件更新。React.memo和React.PureComponent组件功能类似。实际开发中根据需求选择。React.memo官方文档

 

3:React.useMemo

React.useMemo和React.memo不是一回事。React.memo是高阶组件,而React.useMemo是React中的Hook,返回一个memoized值。把创建函数和依赖项数组作为参数传入useMemo,他仅会在依赖项变化时才会重新计算memoized的值。通俗理解就是useMemo返回一个缓存的变量,只有当依赖项改变时,useMemo返回值才会变。

上代码:

import React, { useState } from 'react';

//父组件
function App() {

  const [a, setA] = useState(0)
  const [name, setName] = useState('Durant')
  const [age, setAge] = useState(30)
  const durant = { name, age }


  return (
    <div>
      <h1>a: {a}</h1>
      <button onClick={() => setA(a + 1)}>点击a</button>
      <AfterMemo durant={durant}></AfterMemo>
    </div>
  );
}

//子组件
const Memo = () => {
  return (
      <div>
          { console.log('this is memo Component======') }
      </div>
  )
}

const AfterMemo = React.memo(Memo)



export default App;

代码中,durant是一个对象,传递给子组件。此时点击按钮改变a,会发现子组件重新渲染了:

因为每次改变a,父组件重新渲染都会创建一个新的durant对象,所以子组件重新渲染。但是durant对象的属性和值都没有变化,子组件不应该重新渲染。此时useMemo派上用场了。

import React, { useState } from 'react';

//父组件
function App() {

  const [a, setA] = useState(0)
  const [name, setName] = useState('Durant')
  const [age, setAge] = useState(30)
  const durant = React.useMemo(() => ({ name, age }), [name, age])


  return (
    <div>
      <h1>a: {a}</h1>
      <button onClick={() => setA(a + 1)}>点击a</button>
      <AfterMemo durant={durant}></AfterMemo>
    </div>
  );
}

//子组件
const Memo = () => {
  return (
      <div>
          { console.log('this is memo Component======') }
      </div>
  )
}

const AfterMemo = React.memo(Memo)



export default App;

useMemo函数会返回一个对象,只有当name或age变化时返回的值才会改变。此时改变a,子组件接收到的durant没变,不会重新渲染:

这就是useMemo。详细请看useMemo官方文档

 

4:React.useCallback

useMemo缓存的是一个对象,而useCallback缓存的是一个函数。

import React, { useState } from 'react';

//父组件
function App() {

  const [a, setA] = useState(0)
  const [b, setB] = useState(30)
  const printB = () => {
    console.log('b========', b)
  }


  return (
    <div>
      <h1>a: {a}</h1>
      <button onClick={() => setA(a + 1)}>点击a</button>
      <AfterMemo printB={printB}></AfterMemo>
    </div>
  );
}

//子组件
const Memo = () => {
  return (
      <div>
          { console.log('this is memo Component======') }
      </div>
  )
}

const AfterMemo = React.memo(Memo)



export default App;

此时改变a,父组件重新渲染,创建了一个新的printB函数,子组件刷新:

给printB用useCallback包一层:

import React, { useState } from 'react';

//父组件
function App() {

  const [a, setA] = useState(0)
  const [b, setB] = useState(30)
  const printB = React.useCallback(() => {
    console.log('b======', b)
  }, [b])


  return (
    <div>
      <h1>a: {a}</h1>
      <button onClick={() => setA(a + 1)}>点击a</button>
      <AfterMemo printB={printB}></AfterMemo>
    </div>
  );
}

//子组件
const Memo = () => {
  return (
      <div>
          { console.log('this is memo Component======') }
      </div>
  )
}

const AfterMemo = React.memo(Memo)



export default App;

此时再点击按钮改变a:

可以看到,改变a,子组件不再刷新。但当改变b时,子组件刷新。useCallback官方文档

 

5:总结

1. useCallback(fn, deps)相当于useMemo( () => fn, deps)。

2. 可以把useMemo当作性能优化的手段,但不要把他当成语义上的保证。

3. React.memo仅作为性能优化的方式存在,不要依赖它来阻止重新渲染,这会产生bug。

4. useMemo缓存变量,useCallback缓存函数。

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jsonbro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值