Bootstrap

react中使用useMemo和useCallback

之前学到的memo是用来优化函数组件的重渲染问题,当传入的属性值都没变化时就不会触发组件的重渲染,否则组件就会重渲染。和类组件中的PureComponent组件是类似。

useMemo功能是判断组件中的函数逻辑是否重新执行,用来优化性能。

import React, { useState, useMemo } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

function Count(props) {
  return <div>子组件count值:{props.count}</div>
}

function App(props) {

  const [count, setCount] = useState(0);

  //第一个参数是要执行的函数
  //第二个参数是执行函数依赖的变量组成的数据
  //这里只有count发生变化double才会重新计算
  const double = useMemo(() => {
    return count * 2;
  }, [count])

  return (
    <div className="app">
      <p>父组件count值:{count}</p>
      <Count count={count} />
      <button
        onClick={() => {
          setCount(count + 1)
        }}>
        Add
     </button>
      <div>double值:{double}</div>
    </div>
  )
}

export default App;

效果:
在这里插入图片描述
现在修改传入函数的值:

  const double = useMemo(() => {
    return count * 2;
  }, [count===3])

效果:
在这里插入图片描述
count===3]会从false变为true,再变成false。中间发生了两次变化。所以double的值发生了两次变化。

接着优化组件渲染问题:

import React, { useState, memo, useMemo } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

const Count = memo(function Count(props) {
  console.log('count render')
  return <div>子组件count值:{props.double}</div>
})

function App(props) {

  const [count, setCount] = useState(0);

  //第一个参数是要执行的函数
  //第二个参数是执行函数依赖的变量组成的数据
  //这里只有count发生变化double才会重新计算
  const double = useMemo(() => {
    return count * 2;
  }, [count === 3])

  return (
    <div className="app">
      <p>父组件count值:{count}</p>
      <Count double={double} />
      <button
        onClick={() => {
          setCount(count + 1)
        }}>
        Add
     </button>
      <div>double值:{double}</div>
    </div>
  )
}

export default App;

效果:
在这里插入图片描述
接着向Count子组件中传入onClick方法,看Count子组件是否会被重新渲染:

import React, { useState, memo, useMemo } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

const Count = memo(function Count(props) {
  console.log('count render')
  return <div onClick={props.onClick}>子组件count值:{props.double}</div>
})

function App(props) {

  const [count, setCount] = useState(0);

  //第一个参数是要执行的函数
  //第二个参数是执行函数依赖的变量组成的数据
  //这里只有count发生变化double才会重新计算
  const double = useMemo(() => {
    return count * 2;
  }, [count === 3])

  const onClick = () =>{
    console.log('click')
  }
  return (
    <div className="app">
      <p>父组件count值:{count}</p>
      <Count double={double} onClick={onClick}/>
      <button
        onClick={() => {
          setCount(count + 1)
        }}>
        Add
     </button>
      <div>double值:{double}</div>
    </div>
  )
}

export default App;

效果:
在这里插入图片描述
App父组件发生变化重新渲染后,onClick句柄也发生了变化,导致Count子组件每次也会被重渲染,即使传入的double值没有发生变化。此时就需要让onClick句柄不发生变化。让onClick为useMemo的返回值:

  // const onClick = () =>{
  //   console.log('click')
  // }

  //这样返回的函数就会在组件重渲染时产生相同的句柄
  const onClick = useMemo(() => {
    //这里返回的依然是函数
    return () => {
      console.log('click')
    }
  }, []);

这次当传入Count组件的count和onClick都没有发生变化时就不会做无意义的重渲染:
在这里插入图片描述
如果useMemo返回的是一个函数,则可以用useCallback省略顶层的函数。 将包裹的onClick函数用useCallback包裹:

  // const onClick = () =>{
  //   console.log('click')
  // }

  //这样返回的函数就会在组件重渲染时产生相同的句柄
  //useMemo使用
  // const onClick = useMemo(() => {
  //   //这里返回的依然是函数
  //   return () => {
  //     console.log('click')
  //   }
  // }, []);

  //useCallback使用
  const onClick = useCallback(() => {
    console.log('click')
  }, []);

因此useCallbackuseMemo的变体。

useMemo(()=>return fn);
//等价
useCallback(fn);
;