通过八个例子了解 React.memo,useMemo,useCallback 的用法

285 阅读2分钟

demo1:一个正常的父子组件

const Son = () => {
  console.log('son');
  return <div>son</div>;
};

const Parent: React.FC = props => {
  console.log('parent');
  const [count, setCount] = useState<number>(0);
  return (
    <div>
      <div onClick={() => setCount(prev => prev + 1)}>{count}</div>
      <Son />
    </div>
  );
};

点击 count,输出 parent,还会输出 son。

Son 每次都跟随 Parent 重渲染完全没有必要,于是优化就开始了。

demo2:另一个正常的父子组件

const Son = () => {
  console.log('son');
  return <div>son</div>;
};

const Parent: React.FC = props => {
  console.log('parent');
  const [count, setCount] = useState<number>(0);

  return (
    <div>
      <div onClick={() => setCount(prev => prev + 1)}>{count}</div>
      {props.children}
    </div>
  );
};

const App = () => {
	return (
		<Parent>
			<Son />
		</Parent>
	)
}

点击 count,只输出 parent,不输出 son。

也算是一种方法,但如果 Son 组件依赖于 Parent 的状态就不适用了。

demo3:React.memo() 用于优化性能

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

const Parent: React.FC = props => {
  console.log('parent');
  const [count, setCount] = useState<number>(0);

  return (
    <div>
      <div onClick={() => setCount(prev => prev + 1)}>{count}</div>
      <Son />
    </div>
  );
};

点击 count,只输出 parent,不输出 son。

效果类似于 pureComponent,也可以通过设置第二个参数实现 shouldComponentUpdate 的作用。

但是它只对比 props,而且有不生效的例外情况。

demo4:React.memo() 例外情况一

const Son: React.FC<{ normalFn: Function }> = React.memo(props => {
  console.log('son');
  return <div>son</div>;
});

const Parent: React.FC = props => {
  console.log('parent');
  const [count, setCount] = useState<number>(0);

  const normalFn = () => {};

  return (
    <div>
      <div onClick={() => setCount(prev => prev + 1)}>{count}</div>
      <Son normalFn={normalFn} />
    </div>
  );
};

点击 count,输出 parent,还会输出 son。

父组件每次渲染,都会生成一个新的 normalFn 的实例,导致重复渲染。

demo5:useCallback 用于优化性能

const Son: React.FC<{ normalFn: Function }> = React.memo(props => {
  console.log('son');

  return <div>son</div>;
});

const Parent: React.FC = props => {
  console.log('parent');
  const [count, setCount] = useState<number>(0);

  const normalFn = useCallback(() => {}, []);

  return (
    <div>
      <div onClick={() => setCount(prev => prev + 1)}>{count}</div>
      <Son normalFn={normalFn} />
    </div>
  );
};

点击 count,只输出 parent,不输出 son。

useCallback 用于缓存函数。

demo6:React.memo() 例外情况二

const Son: React.FC<{ normalFn: Function; normalValue: object }> = React.memo(props => {
  console.log('son');

  return <div>son</div>;
});

const Parent: React.FC = props => {
  console.log('parent');
  const [count, setCount] = useState<number>(0);

  const normalValue = {
    a: 'b',
  };
  const normalFn = useCallback(() => {}, []);

  return (
    <div>
      <div onClick={() => setCount(prev => prev + 1)}>{count}</div>
      <Son normalValue={normalValue} normalFn={normalFn} />
    </div>
  );
};

点击 count,输出 parent,还会输出 son。

同样的,父组件每次渲染,也会生成新的 normalValue 对象。

注意:这里如果 normalValue 不是引用类型的话就不会出现重渲染的问题。

demo7:useMemo 用于优化性能

const Son: React.FC<{ normalFn: Function; normalValue: object }> = React.memo(props => {
  console.log('son');

  return <div>son</div>;
});

const Parent: React.FC = props => {
  console.log('parent');
  const [count, setCount] = useState<number>(0);

  const normalValue = useMemo(() => {
    return {
      a: 'b',
    };
  }, []);
  const normalFn = useCallback(() => {}, []);

  return (
    <div>
      <div onClick={() => setCount(prev => prev + 1)}>{count}</div>
      <Son normalValue={normalValue} normalFn={normalFn} />
    </div>
  );
};

点击 count,只输出 parent,不输出 son。

useMemo() 用于缓存值。

demo8:对象尽量使用 useState 控制

const Son: React.FC<{ normalFn: Function; normalValue: object }> = React.memo(props => {
  console.log('son');
  return <div>son</div>;
});

const Parent: React.FC = props => {
  console.log('parent');
  const [count, setCount] = useState<number>(0);


  const [normalValue, setNormalValue] = useState({
    a: 'b',
  });
  const normalFn = useCallback(() => {}, []);

  return (
    <div>
      <div onClick={() => setCount(prev => prev + 1)}>{count}</div>
      <Son normalValue={normalValue} normalFn={normalFn} />
    </div>
  );
};

点击 count,只输出 parent,不输出 son。