深入理解useMemo与useCallBack的区别

深入理解useMemo与useCallBack的区别

前言:在阅读本文前需了解基本数据类型和引用数据类型的区别。

true === true       // true
false === false     // true
1 === 1             // true
'a' === 'a'         // true

{} === {}                 // false
[] === []                 // false
() => {} === () => {}     // false

目录

  • React.memo()
  • React.useCallback()
  • React.useMemo()

*以下所有例子将采用函数式组件进行说明

React.memo()

Q: React中当组件的props或state变化时,会重新渲染视图,但实际开发中并不需要多次的渲染,直接导致的结果是用户的体验感不好。
子组件

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>
  );
}

结果:子组件信息被多次打印
在这里插入图片描述

A:将子组件用React.memo()包一层,只用当子组件的props和state变化时,才会从新渲染子组件。

子组件(写法一)

import React, { memo } from 'react'

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

子组件(写法二)

import React, { memo } from 'react'

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

ChildComp = memo(ChildComp)

结果:子组件信息只在在父组件被初次渲染的时候打印了一次
在这里插入图片描述

React.useCallback()

上述例子子组件并没有接收任何的参数,如果接收参数React.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>
  );
}

结果:子组件的信息仍然打印了多次
在这里插入图片描述
原因:

  • 点击按钮导致父组件重新渲染(count值发生了变化),并且会重新创建changeName函数(函数也属于引用数据类型,每次重新创建时地址不同),子组件的属性(函数)发生了变化,因此子组件被重新渲染

解决:修改父组件的changeName方法,用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()的函数也不会重新创建

React.useMemo()

Q:父组件传递给子组件的数据不是字符串而是对象,React.Memo是否还有用?
子组件

import React, { memo } from 'react'

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

父组件

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>
  );
}

结果:子组件信息被打印了多次
在这里插入图片描述
原因:跟传递函数原因类似,当点击按钮时,父组件会被重新渲染,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>
  );
}

总结

  • 上述三种方法都是为了解决组件不必要的重复渲染问题 ,但针对的情况有所不同
  • React.Memo(),前提是子组件没有接收父组件传递的引用类型参数(函数,对象,数组),使用:用React.Memo包裹子组件
  • React.useCallBack(),当子组件接收来自父组件的函数,使用:将函数用React.useCallback(函数,[])包裹
  • React.useMemo(),当子组件接收来自父组件传递的值是对象,使用:将对象用React.useMemo(()=>{对象},[监听的值])进行包裹

原文地址:https://www.jianshu.com/p/014ee0ebe959

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
useMemouseCallback是React中的两个钩子函数,它们的主要作用是优化性能和避免不必要的重新渲染。 useMemo的基本思想是在组件渲染时缓存计算结果,只有当依赖项发生变化时才重新计算。这可以用于避免重复计算代价高昂的函数或复杂的计算逻辑。 示例1中展示了当有大量计算需要处理时,可以使用useMemo来缓存计算结果,以避免重复计算。示例2中展示了如何使用useMemo来保留引用,以避免在依赖项变化时重新创建对象。 useCallback的主要目的是避免不必要的函数重复创建,特别是在传递给子组件时。它使用了与useMemo类似的思想,只有在依赖项发生变化时才返回一个新的函数。这可以提高性能,避免不必要的重新渲染。 在使用这些hook时,可以考虑以下几种情况: 1. 当需要缓存计算结果或保留引用时,可以使用useMemo。 2. 当需要传递一个稳定的函数给子组件时,可以使用useCallback。 总结来说,useMemouseCallback是React中的性能优化工具,可以帮助我们在合适的时机缓存计算结果和避免不必要的函数重新创建。正确理解和使用它们可以有效提高应用程序的性能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [深入了解 useMemouseCallback](https://blog.csdn.net/p1967914901/article/details/126973032)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [useMemouseCallback简单理解](https://blog.csdn.net/qq_34629352/article/details/124419523)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值