zoukankan      html  css  js  c++  java
  • 介绍React.memo, useMemo 和 useCallback

    什么是 React.memo ?

    React.memo 和 React.PureComponent 类似, React.PureComponent 在类组件中使用,而React.memo 在函数组件中使用

    看下面两个例子,有两个计数器组件,两个计数器的数据都来源于父组件,第一个计数器通过点击按钮数字会不断累加而发生改变,第二个计数器没有按钮控制数字改变。

    const App = () => {
        const [count1, setCount1] = React.useState(0)
        const [count2, setCount2] = React.useState(0)
    
        const increaseCounter1 = () => {
            setCount1(count1 => count1 + 1)
        }
    
        return (
            <>
                <button onClick={increaseCounter1}>Increase counter 1</button>
                <Counter value={count1}>Counter 1</Counter>
                <Counter value={count2}>Coutner 2</Counter>
            </>
        )
    }
    

    计数器组件

    const Counter = ({value, children}) => {
        console.log('Render: ', children)
    
        return (
            <div>
                {children}: {value}
            </div>
        )
    }
    
    export default Counter
    

    但是不管这个两个计算器的数据是否发生改变,Counter 组件都会输出 Render 来。现在我们尝试使用 React.memo 包裹这个组件。

    const Counter = ({value, children}) => {
        console.log('Render: ', children)
    
        return (
            <div>
                {children}: {value}
            </div>
        )
    }
    
    export default React.memo(Counter)
    

    React.memo 浅层对比 prop 和 state 的方式来实现了该函数。现在开始第二个计数器将不会重新渲染了,由于 prop 没有发生改变。

    在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。

    如要避免不必要的子组件的重渲染,你需要在所有可能的地方使用 PureComponent,或是手动实现 shouldComponentUpdate 方法。

    useMemo and useCallback

    useMemo 相当于Vue中computed里的计算属性,当某个依赖项改变时才重新计算值,这种优化有助于避免在每次渲染时都进行高开销的计算。

    React.useMemo(() => {
      fooFunction()
    }, [dependencies])
    
    React.useCallback(() => {
      fooFunction()
    }, [dependencies])
    

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

    useCallback 和 useMemo 参数相同,第一个参数是函数,第二个参数是依赖项的数组。主要区别是 React.useMemo 将调用 fn 函数并返回其结果,而 React.useCallback 将返回 fn 函数而不调用它。

    看看下来例子

    const App = () => {
        const fooFunction = () => {
            return 'Foo is just Food without D'
        }
    
        const useMemoResult = React.useMemo(fooFunction, [])
        const useCallbackResult = React.useCallback(fooFunction, [])
    
        console.log('useMemoResult: ', useMemoResult)
        console.log('useCallbackResult: ', useCallbackResult)
    
        return <p>Foo is just food without D</p>
    }
    

    最后得到的输出结果
    在这里插入图片描述

    const Me = ({girlFriendWords}) => {
    
        // Provided that girlFriendWords is a string
    
        const myReply = decideWhatToSay (girlFriendWords)
    
        return <p>{myReply}</p>
    }
    

    代码中计算 myReply 值,默认每次组件渲染的时候都会重新执行

    const Me = ({girlFriendWords}) => {
    
        // Provided that girlFriendWords is a string
    
        const myReply = React.useMemo(() => decideWhatToSay (girlFriendWords), [girlFriendWords])
    
        return <p>{myReply}</p>
    }
    

    使用 React.useMemo 通过 [girlFriendWords] 作为依赖项,当依赖的值发生改变,函数才会重新执行 decideWhatToSay

    useCallback

    使用场景是:有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;

    import React, { useMemo, useCallback } from "react"
    let Counter = ({ value, children, onClick }) => {
      console.log('Render: ', children)
    
      return (
        <div onClick={onClick}>
          {children}: {value}
        </div>
      )
    }
    Counter = React.memo(Counter)
    
    
    
    const App = () => {
      const [count1, setCount1] = React.useState(0)
      const [count2, setCount2] = React.useState(0)
    
      const increaseCounter1 = () => {
        setCount1(count1 => count1 + 1)
      }
      const increaseCounter2 = () => {
        setCount2(count2 => count2 + 1)
      }
    
      return (
        <>
          <Counter value={count1} onClick={increaseCounter1}>Counter 1</Counter>
          <Counter value={count2} onClick={increaseCounter2}>Coutner 2</Counter>
        </>
      )
    }
    
    export default App
    
    

    但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。

    import React, { useMemo, useCallback } from "react"
    let Counter = ({ value, children, onClick }) => {
      console.log('Render: ', children)
    
      return (
        <div onClick={onClick}>
          {children}: {value}
        </div>
      )
    }
    Counter = React.memo(Counter)
    
    
    
    const App = () => {
      const [count1, setCount1] = React.useState(0)
      const [count2, setCount2] = React.useState(0)
    
      const increaseCounter1 = useCallback(() => {
        setCount1(count1 => count1 + 1)
      }, [])
      const increaseCounter2 = useCallback(() => {
        setCount2(count2 => count2 + 1)
      }, [])
      console.log(increaseCounter1)
    
      return (
        <>
          <Counter value={count1} onClick={increaseCounter1}>Counter 1</Counter>
          <Counter value={count2} onClick={increaseCounter2}>Coutner 2</Counter>
        </>
      )
    }
    
    export default App
  • 相关阅读:
    【Android游戏开发之八】游戏中添加音频详解MediaPlayer与SoundPool的利弊以及各个在游戏中的用途!
    【Android游戏开发之九】(细节处理)触屏事件中的Bug解决方案以及禁止横屏和竖屏切换!
    【Android游戏开发之七】(游戏开发中需要的样式)再次剖析游戏开发中对SurfaceView中添加组件方案!
    前端要给力之:URL应该有多长?
    【Android游戏开发之三】剖析 SurfaceView ! Callback以及SurfaceHolder!!
    charactersFound方法中的陷阱
    前端要给力之:分解对象构造过程new()
    结合UIImageView实现图片的移动和缩放
    【Android游戏开发之一】设置全屏以及绘画简单的图形
    扩展BaseAdapter实现在ListView中浏览文件
  • 原文地址:https://www.cnblogs.com/jianxian/p/12535227.html
Copyright © 2011-2022 走看看