react系列 UseMemo 和 memo

218 阅读3分钟

一、useMemo是什么

useMemo 是一个有记忆的钩子函数。它会记住函数返回的值useMemo接受一个回调函数和一个依赖项(就好比useEffects),并返回一个记住的值。只有当传递的任何依赖项的值发生变化时,它才会导致组件重新呈现。

const complexFunction = useMemo(() => {
  ...
},[replyValue]);

什么时候使用useMemo

只在函数运行非常昂贵的计算时才使用这个hooks

usememo如何工作的示例

在页面上新增了两个button

firstbutton通过addNum函数不断乘自身,并显示值在左侧。

secondbutton不断累加1,并显示值在左侧。

import React, { useState } from 'react';
import { render } from 'react-dom';


const Parent = () => {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(0);

  console.log('父组件触发');

  const addNum = (n) => {
    //无论是count变化还是count2变化都重新触发了该方法
    console.log('first触发');
    return n * n;
  };

  const value = addNum(count);
  return (
    <main>
      <p>
        FirstCount: {value}{' '}
        <button onClick={() => setCount((count) => count + 1)}>first*first</button>{' '}
      </p>
      <p>
        SecondCount: {count2}{' '}
        <button onClick={() => setCount2((count2) => count2 + 1)}>
          second +1
        </button>
      </p>

    </main>
  );
};

render(<Parent />, document.getElementById('root'));

useMemo.gif

问题:点击second+1的button也会触发addNum进行重新计算,如果addNum一旦是很复杂的计算函数,则可能导致性能问题。

使用useMemo优化性能

import React, { useState,useMemo } from 'react';
import { render } from 'react-dom';


const Parent = () => {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(0);

  console.log('父组件触发');

  const addNum = (n) => {
    //只有是count变化才会触发该方法
    console.log('first触发');
    return n * n;
  };

  // const value = addNum(count);
  const value = useMemo(() => addNum(count), [count]);
  return (
    <main>
      <p>
        FirstCount: {value}{' '}
        <button onClick={() => setCount((count) => count + 1)}>first*first</button>{' '}
      </p>
      <p>
        SecondCount: {count2}{' '}
        <button onClick={() => setCount2((count2) => count2 + 1)}>
          second +1
        </button>
      </p>

    </main>
  );
};

render(<Parent />, document.getElementById('root'));

useMemo2.gif

二、memo是什么

memouseMemo 都是是一个有记忆的钩子函数。useMemo它会记住函数返回的值。而memo它是作用于组件上,同样接受一个回调函数和一个依赖项(这个依赖项是接受true或者false)。只有当传递的任何依赖项的值发生变化时,它才会导致组件重新呈现。

Memo如何工作的示例

在上文代码基础上,加入了一个<Child />,并且把count2的值传递给<Child count2 ={count2} />

import React, { useState, useMemo } from 'react';
import { render } from 'react-dom';

const Parent = () => {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(0);

  console.log('父组件触发');

  const addNum = (n) => {
    console.log('first触发');
    return n * n;
  };

  const value = useMemo(() => addNum(count), [count]);
  return (
    <main>
      <p>
        FirstCount: {value}{' '}
        <button onClick={() => setCount((count) => count + 1)}>first*first</button>{' '}
      </p>
      <p>
        SecondCount: {count2}{' '}
        <button onClick={() => setCount2((count2) => count2 + 1)}>
          second +1
        </button>
      </p>
      <Child count2={count2}/>

    </main>
  );
};

const Child = ({count2}) => {
 //无论是更新count1还是count2子组件都会重新渲染
  console.log("子组件重新渲染")
  return (
    <div>
      <p>孩子组件:{count2}</p>
    </div>
  );
};


render(<Parent />, document.getElementById('root'));

Memo1.gif

问题:点击frist*first按钮时也会触发<Child />的重新渲染,如果<Child /> Dom比较复杂,每次频繁渲染则可能导致性能问题。

使用Memo优化性能。只在secondCount变化才会触发<Child />组件重新渲染。

import React, { useState, useMemo,memo } from 'react';
import { render } from 'react-dom';



const Parent = () => {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(0);

  console.log('父组件触发');

  const addNum = (n) => {
    console.log('first触发');
    return n * n;
  };

  const value = useMemo(() => addNum(count), [count]);
  return (
    <main>
      <p>
        FirstCount: {value}{' '}
        <button onClick={() => setCount((count) => count + 1)}>first*first</button>{' '}
      </p>
      <p>
        SecondCount: {count2}{' '}
        <button onClick={() => setCount2((count2) => count2 + 1)}>
          second +1
        </button>
      </p>
      <Child count2={count2}/>

    </main>
  );
}

const isEqual = (pre,cur)=>{
  if(pre.count2!==cur.count2){
   //更新Child
    return false
  }
  //不更新Child
  return true

}

const Child = memo((props) => {
//只有count2改变才会触发更新
  console.log("子组件重新渲染")
  return (
    <div>
      <p>孩子组件:{props.count2}</p>
    </div>
  );
},isEqual);


render(<Parent />, document.getElementById('root'));

Memo2.gif

注意事项

UseMemo或者memo都是使用了一些复杂的逻辑,所以过度使用它可能会对组件造成弊大于利。此外,在react文档中指出,react有时可能会选择忘记一些以前记住的值,并在下一次渲染时重新计算它们,这样即使没有UseMemo或者memo,您的组件可能比使用这两个钩子时运行得更快。

END