React-useMemo

useMemo

useMemo 是一个 React Hook,它在每次重新渲染的时候能够缓存计算的结果。

使用方式为

const cachedValue = useMemo(calculateValue, dependencies)

传入的参数第一个是对依赖值进行计算的函数。该函数没有任何参数,并且返回值是 useMemo 的缓存值 cachedValue 。

第二个参数是依赖值数组,里面存入的是在组件中,并且在函数calculateValue计算需要用到的React响应式变量。React 使用 Object.is 将每个依赖项与其之前的值进行比较。

useMemo 的返回值就是依赖项 dependencies 经过函数 calculateValue 计算的值。在初次渲染中,cachedValue 就是函数 calculateValue 计算的值。在接下来的渲染中,如果依赖项 dependencies 没有变化,cachedValue 就是上次缓存的值;如果依赖项 dependencies 发生变化,cachedValue 就会经过 calculateValue 重新计算得到。

注意事项:

  • useMemo 是一个 React Hook,所以你只能 在组件的顶层 或者自定义 Hook 中调用它。不能在循环语句或条件语句中调用它。

组件中的使用方式

import { useState, useMemo } from "react"

function factorialOf(n) {
  console.log('斐波那契函数执行了')
  return n <= 0 ? 1 : n * factorialOf(n - 1)
}

export default function UseMemoDemo () {
  const [count, setCount] = useState(0)

  // 用 useMemo 
  const sumByCount = useMemo(() => factorialOf(count), [count])

}

缓存计算值

import { useState, useMemo } from "react"

function factorialOf(n) {
  console.log('斐波那契函数执行了')
  return n <= 0 ? 1 : n * factorialOf(n - 1)
}

export default function UseMemoDemo () {
  const [count, setCount] = useState(0)
  // 计算斐波那契之和
  const sumByCount = factorialOf(count)

  const [num, setNum] = useState(0)

  return (
    <>
      <div>{sumByCount}</div>
      <div>
        <button onClick={() => setCount(count + 1)}>+count:{count}</button>
        <button onClick={() => setNum(num + 1)}>+num:{num}</button>
      </div>
    </>
  )
}

当触发setCount或setNum的时候,就会重新渲染,从而会重新执行 sumByCount 的计算。但是 sumByCount 只依赖于 count 。当触发 SetNum 的时候,sumByCount 就做了一次无效计算。这时候可以用 useMemo 将 sumByCount 的值缓存起来,当依赖项 count 发生变化的时候,再重新计算 sumByCount 的值。当 count 的值与上次渲染时相同,那么就可以重用之前计算过的 sumByCount 。

import { useState, useMemo } from "react"

function factorialOf(n) {
  console.log('斐波那契函数执行了')
  return n <= 0 ? 1 : n * factorialOf(n - 1)
}

export default function UseMemoDemo () {
  const [count, setCount] = useState(0)
  // 计算斐波那契之和
  // const sumByCount = factorialOf(count)

  // 用 useMemo 
  const sumByCount = useMemo(() => factorialOf(count), [count])

  const [num, setNum] = useState(0)

  return (
    <>
      <div>{sumByCount}</div>
      <div>
        <button onClick={() => setCount(count + 1)}>+count:{count}</button>
        <button onClick={() => setNum(num + 1)}>+num:{num}</button>
      </div>
    </>
  )
}

记忆另一个Hook的依赖项

import { useState, useMemo } from "react"

function factorialOf(n) {
  console.log('斐波那契函数执行了')
  return n <= 0 ? 1 : n * factorialOf(n - 1)
}

export default function UseMemoDemo () {
    ...
    
  // 记录另一个Hook的依赖
  const searchItem = [
    {label: 'apple', key: 'apple'},
    {label: 'banana', key: 'banana'},
    {label: 'orange', key: 'orange'},
  ]

  const chosenItem = useMemo(() => {
    console.log('计算chosenItem')
    return searchItem[0].key
  }, [searchItem])

  return (
    <>
      <div>{sumByCount}</div>
      <div>
        <button onClick={() => setCount(count + 1)}>+count:{count}</button>
        <button onClick={() => setNum(num + 1)}>+num:{num}</button>
      </div>
      <div>
        key: {chosenItem}
      </div>
    </>
  )
}

现在 chosenItem 依赖于 searchItem 。但是React每次渲染就像一张快照,也就是说每次渲染函数执行的时候,函数都会拥有独立的state和props,函数内部的变量也是独立。所以每次渲染中 searchItem 的引用都是不同的。那么,在每次渲染中,useMemo都会判断 searchItem 发生了变化,从而重新执行 chosenItem 的计算。

在这里插入图片描述

为了避免无效的重新计算,可以将 searchItem 缓存起来,也可以通过 useRef 保存。不过当 searchItem 会依赖于组件中的props时候,建议采取 useMemo 进行缓存。

import { useState, useMemo } from "react"

function factorialOf(n) {
  console.log('斐波那契函数执行了')
  return n <= 0 ? 1 : n * factorialOf(n - 1)
}

export default function UseMemoDemo () {
	...

  // 记录另一个Hook的依赖
  // const searchItem = [
  //   {label: 'apple', key: 'apple'},
  //   {label: 'banana', key: 'banana'},
  //   {label: 'orange', key: 'orange'},
  // ]

  const searchItem = useMemo(() => {
    return [
      {label: 'apple', key: 'apple'},
      {label: 'banana', key: 'banana'},
      {label: 'orange', key: 'orange'},
    ]
  }, [])

  const chosenItem = useMemo(() => {
    console.log('计算chosenItem')
    return searchItem[0].key
  }, [searchItem])

  return (
    <>
      <div>{sumByCount}</div>
      <div>
        <button onClick={() => setCount(count + 1)}>+count:{count}</button>
        <button onClick={() => setNum(num + 1)}>+num:{num}</button>
      </div>
      <div>
        key: {chosenItem}
      </div>
    </>
  )
}

在这里插入图片描述

避免组件的重新渲染

import { useState, useMemo } from "react"
import List from "./components/List"

function factorialOf(n) {
  console.log('斐波那契函数执行了')
  return n <= 0 ? 1 : n * factorialOf(n - 1)
}

export default function UseMemoDemo () {
  // useMemo缓存计算值
  const [count, setCount] = useState(0)
  // 计算斐波那契之和
  // const sumByCount = factorialOf(count)

  // 用 useMemo 
  const sumByCount = useMemo(() => factorialOf(count), [count])

  const [num, setNum] = useState(0)

  // 记录另一个Hook的依赖
  // const searchItem = [
  //   {label: 'apple', key: 'apple'},
  //   {label: 'banana', key: 'banana'},
  //   {label: 'orange', key: 'orange'},
  // ]

  const searchItem = useMemo(() => {
    return [
      {label: 'apple', key: 'apple'},
      {label: 'banana', key: 'banana'},
      {label: 'orange', key: 'orange'},
    ]
  }, [])

  const chosenItem = useMemo(() => {
    console.log('计算chosenItem')
    return searchItem[0].key
  }, [searchItem])

  // 避免重新渲染

  return (
    <>
      <div>{sumByCount}</div>
      <div>
        <button onClick={() => setCount(count + 1)}>+count:{count}</button>
        <button onClick={() => setNum(num + 1)}>+num:{num}</button>
      </div>
      <div>
        key: {chosenItem}
      </div>
      {/* 避免重新渲染 */}
      <div>
        <List searchList={searchItem}></List>
      </div>
    </>
  )
}
// List.jsx
export default function List ({ searchList}) {
  console.log('List组件渲染了')
  return (
    <ul>
      {searchList.map(item => {
        return <li key={item.key}>{item.label}</li>
      })}
    </ul>
  )
}

在这里插入图片描述

现在 searchItem 作为props传递给子组件List。但是当我们触发 setCount 或 setNum 的时候,父组件重新渲染,子组件 List 也会跟着重新渲染。这是因为默认情况下,当一个组件重新渲染时,React 会递归地重新渲染它的所有子组件。当我们不希望子组件 List 在props不变的情况下跟着重新渲染,可以将子组件 List 包装在 memo 中,这样当它的 props 跟上一次渲染相同的时候它就会跳过本次渲染:

import { memo } from 'react'

const List =  memo(function List ({ searchList}) {
  console.log('List组件渲染了')
  return (
    <ul>
      {searchList.map(item => {
        return <li key={item.key}>{item.label}</li>
      })}
    </ul>
  )
})

export default List

在这里插入图片描述

总结

  • useMemo 可以将依赖项经过一些计算得到的值缓存起来,直到依赖项发生变化的时候才需要重新计算;
  • useMemo 类似于vue中的计算属性 computed ;
  • 用useMemo缓存的值需要传给子组件的时候,可以对子组件用 memo 包装,从而避免子组件的重新渲染;
  • 10
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值