性能优化之 React.memo、useMomo、useCallback

241 阅读2分钟

React.memo

React.memo 是一个高阶组件,对传入的 props 进行浅比较,从而来决定是否更新被包裹的组件。

import React, { useState } from "react";

const Child =() => {
  console.log("render Child");
  return <div>Child</div>
};

const Parent = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <Child />
      <div>count:{count}</div>
      <button
        onClick={() => {
          setCount(count+1)
        }}
      >
        Add
      </button>
    </div>
  );
};

export default Parent;

image.png

image.png

如上图,父组件的 count 和 Child 组件没有关系,当我点击“Add”的时候,控制台却打印出 "render Child" 的信息,我们不希望子组件更新,这时候我们就可以用React.memo

import React, { useState } from "react";

const Child = React.memo(() => {
  console.log("render Child");
 return <div>Child</div>
});

const Parent = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <Child />
      <div>count:{count}</div>
      <button
        onClick={() => {
          setCount(count+1)
        }}
      >
        Add
      </button>
    </div>
  );
};

export default Parent;

注意:React.memo 对于 props 的比较是浅比较,当一个引用类型的 props 改变时,只要它的地址没有发生改变,就算 props 中某一项数据发生了改变,被 memo 包裹的组件也不会重新渲染。

useMomo

如果一些值的计算量很大,那么可以用 useMemo来做一个缓存,只有依赖变化时才会重新计算,而不是每次渲染时都进行计算。

import React, { useState } from "react";

const Child = (props) => {
  const { age = 0,name='' } = props;
  const someComputer = ()=>{
    console.log('some computer');
    return age+10
  }
  return <div>
      <div>age:{age}</div>
      <div>name:{name}</div>
      <div>{someComputer()}</div>
    </div>;
};

const Parent = () => {
  const [age,setAge] = useState(0);
  const [name,setName] = useState('张三');
  return (
    <div className={styles.home}>
      <Child age={age} name={name}/>
      <button
        onClick={() => {
         setAge(age+1)
        }}
      >
        Age加1
      </button>
      <button
        onClick={() => {
          setName('李四')
        }}
      >
        改名
      </button>
    </div>
  );
};

export default Parent;

image.png
上面的例子中,当我们点击“改名”按钮时,控制台打印了“some computer”,其实 Child 组建的 someComputer 并不需要更新,因为 someComputer 只依赖了 “age”,这时可以使用useMemo,

import React, { useState,useMemo } from "react";

const Child = (props) => {
  const { age = 0,name='' } = props;
  const someComputer = useMemo(()=>{
    console.log('some computer');
    return age+10
  },[age])
  return <div>
      <div>age:{age}</div>
      <div>name:{name}</div>
      <div>{someComputer()}</div>
    </div>;
};

const Parent = () => {
  const [age,setAge] = useState(0);
  const [name,setName] = useState('张三');
  return (
    <div>
      <Child age={age} name={name}/>
      <button
        onClick={() => {
         setAge(age+1)
        }}
      >
        Age加1
      </button>
      <button
        onClick={() => {
          setName('李四')
        }}
      >
        改名
      </button>
    </div>
  );
};

export default Parent;

useCallback

useCallback 类似于 useMemo,只不过 useCallback 用于缓存函数,对于需要传递函数给子组件的场合,不使用 useCallback的话,子组件每次都会重新渲染

import React, { useState } from "react";

const Child = React.memo(() => {
  console.log('render Child');
  return <div>Child</div>;
});

const Parent = () => {
  const [count,setCount] = useState(0);
  const myOnClick = ()=>{}
  return (
    <div>
      <Child myOnClick={myOnClick}/>
      <button onClick={()=>{setCount(count+1)}}>加一</button>
    </div>
  );
};

export default Parent;

image.png

上面的代码中,当我们点击“加一”按钮时,控制台打印“render Child”,是因为我们更新了 Parent 组件的“count”值,从而导致 myOnClick 函数被重新生成,其实 myOnClick 并没有依赖 “count”,我们可以使用“useCallback”,进行优化,如下:

import React, { useCallback, useState } from "react";

const Child = React.memo(() => {
  console.log('render Child');
  return <div>Child</div>;
});

const Parent = () => {
  const [count,setCount] = useState(0);
  const myOnClick = useCallback(()=>{},[])
  return (
    <div>
      <Child myOnClick={myOnClick}/>
      <button onClick={()=>{setCount(count+1)}}>Add1</button>
    </div>
  );
};

export default Parent;