详解React中的useMemo和useCallback(超白话)

2,908 阅读2分钟

1.首先要了解什么是渲染

简单来说就是开发者不用考虑UI,只考虑修改状态,React会根据状态自动修改UI,渲染就是修改的过程。

2.React的不足

在每一次状态的修改(比如input框中的输入),都会导致重新渲染。要是代码中有一个很复杂的运算,这个运算也会跟着重新渲染。每渲染一次,就重新计算一次,耗时时间非常长(有的运算可能达到2秒之长)。有没有什么方法能够只在用到的时候才渲染呢?

3.useMemo

useMemo能够帮助我们解决这个问题

4.useMemo用法

useMemo(()=>{},[])应该传入两个参数,一是处理函数,二是依赖项,处理函数中返回的应当是一个结果(虽然也可以传函数,但是不建议这么做)

5.useMemo案例

案例1.gif 这个案例是由两部分组成时钟计算正整数包含的所有质数,不加useMemo,每增加1秒,计算就会重新执行。加上useMemo,只用当在Input框内输入数字,才会重新计算。

案例代码

import React, {useMemo} from 'react';
import format from 'date-fns/format';

function App() {
  const [selectedNum, setSelectedNum] = React.useState(100);
  
  const time = useTime();

  const allPrimesRes = useMemo(()=>{
    const allPrimes = [];
    console.log('运行');
    
    for (let counter = 2; counter < selectedNum; counter++) {
      if (isPrime(counter)) {
        allPrimes.push(counter);
      }
    }
    return allPrimes;
  }, [selectedNum])
  
  return (
    <>
      <p className="clock">
        {format(time, 'hh:mm:ss a')}
      </p>
      <form>
        <label htmlFor="num">Your number:</label>
        <input
          type="number"
          value={selectedNum}
          onChange={(event) => {
            // To prevent computers from exploding,
            // we'll max out at 100k
            let num = Math.min(100_000, Number(event.target.value));
            
            setSelectedNum(num);
          }}
        />
      </form>
      <p>
        There are {allPrimesRes.length} prime(s) between 1 and {selectedNum}:
        {' '}
        <span className="prime-list">
          {allPrimesRes.join(', ')}
        </span>
      </p>
    </>
  );
}

function useTime() {
  const [time, setTime] = React.useState(new Date());
  
  React.useEffect(() => {
    const intervalId = window.setInterval(() => {
      setTime(new Date());
    }, 1000);
  
    return () => {
      window.clearInterval(intervalId);
    }
  }, []);
  
  return time;
}

function isPrime(n){
  const max = Math.ceil(Math.sqrt(n));
  
  if (n === 2) {
    return true;
  }
  
  for (let counter = 2; counter <= max; counter++) {
    if (n % counter === 0) {
      return false;
    }
  }

  return true;
}

export default App;

可以看到上面的代码处理计算的函数被useMemo包裹上了,并且在[]里加了依赖项,只有在selectedNum变化时,才会重新计算

image.png

7.useCallback

useCallback和useMemo传入的参数是一样的,区别在于useMemo返回的是传入的函数结果,useCallback返回的是函数本身,也就是说useCallback不用return函数结果。

8.useCallback案例

主组件

import React, {useCallback} from 'react';

import MegaBoost from './MegaBoost';

function App() {
  const [count, setCount] = React.useState(0);

  const handleMegaBoost = useCallback(()=>{
  setCount((currentValue) => currentValue + 1234);
  }, []);

  return (
    <>
      Count: {count}
      <button
        onClick={() => {
          setCount(count + 1)
        }}
      >
        Click me!
      </button>
      <MegaBoost handleClick={handleMegaBoost} />
    </>
  );
}

export default App;

MegaBoost组件

import React from 'react';

function MegaBoost({ handleClick }) {
  console.log('Render MegaBoost');
  
  return (
    <button
      className="mega-boost-button"
      onClick={handleClick}
    >
      MEGA BOOST!
    </button>
  );
}

export default MegaBoost;

上面代码由三个部分组成

image.png 如果不加useCallback,无论点击哪个按钮,MegaBoost组件都会重新渲染,useCallback可以直接返回函数,不用return。

总结

以上就是useMemo和useCallback最简单的概念和用法,更深层次的概念以后会写文章详细说明,还有一些应用上的坑也会进行详解,感谢码友们的阅读。