useMemo和useCallback到底要怎么用

useMemo 和 useCallback 是 React 的内置 Hook,通常作为优化性能的手段被使用。他们可以用来缓存函数、组件、变量,以避免两次渲染间的重复计算。但是实践过程中,他们经常被过度使用:担心性能的开发者给每个组件、函数、变量、计算过程都套上了 mem,有一次我在接手一个项目时,看到一个函数就用useMemo或者useCallback给包起来,人都麻了。这玩意到底怎么用,我说一下我的观点

通常我们用主要原因:
1.防止不必要的渲染
2.防止不必要的effect
3.防止不必要的重复计算
第一种和第三种往往被无用,导致适得其反,代码沉余。

防止不必要的effect
如果一个值被 useEffect 依赖,那它可能需要被缓存,这样可以避免重复执行 effect。

useMemo:

const Component = () => {
  const a = useMemo(() => ({ test: 1 }), []);
  useEffect(() => {
    doSomething();
  }, [a]);
};

useCallback:

const Component = () => {
  const fetch = useCallback(() => {
    console.log('fetch some data here');
  }, []);
  useEffect(() => {
    fetch();
  }, [fetch]);
};

防止不必要的render,首先我们要明确
1.什么时候会render
2.如何防止子组件render
3.如何判断子组件需要缓存

针对这三种情况:
1.当本身的props或者state改变时会render
2.如果有使用到useContext时,当value改变时,使用到value的值的对应的组件也会发生render
3.当父组件重新渲染时,对应的子组件也会render

例如:

const App = () => {
  const [state, setState] = useState(1);

  const onClick = useCallback(() => {
    console.log('Do something on click');
  }, []);

  return (
	// 无论 onClick 是否被缓存,Page 都会 render 
    <Page onClick={onClick} />
  );
};

这里的useCallback是完全无效的

如何让子组件不render呢:
我们必须缓存子组件本身和他身上的绑定事件

const PageMemoized = React.memo(Page);

const App = () => {
  const [state, setState] = useState(1);

  const onClick = useCallback(() => {
    console.log('Do something on click');
  }, []);

  return (
    // Page 和 onClick 同时 memorize
    <PageMemoized onClick={onClick} />
  );
};

由于使用了React.memo,PageMemoized 会浅比较 props 的变化后再决定是否 re-render。onClick 被缓存后不会再变化,所以 PageMemoized 不再 render。

当然 如果在PageMemoized上在添加一个未被缓存的props,那么还是到不到该有的效果,子组件还是会被render

const PageMemoized = React.memo(Page);

const App = () => {
  const [state, setState] = useState(1);

  const onClick = useCallback(() => {
    console.log('Do something on click');
  }, []);

  return (
    <PageMemoized onClick={onClick} value={[1, 2, 3]} />
  );
};

由于 value 会随着 App 的 re-render 重新定义,引用值发生变化,导致 PageMemoized 仍然会触发 render。

那么我们能够知道想要让子组件不被render要满足两个条件:
1.子组件自身被缓存。
2.子组件所有的 prop 都被缓存。

那么如何判断子组件是否需要缓存呢

这个就需要我们开发人员在平时的具体业务中自己去看了,什么时候该用,什么时候不该用
除此之外还有另外一个成本:性能成本。

组件的缓存是在初始化时进行,虽然每个组件缓存的性能耗费很低,通常不足1ms,但大型程序里成百上千的组件如果同时初始化缓存,成本可能会变得很可观。
所以局部使用 memo,比全局使用显的更优雅、性能更好,坏处是需要开发者主动去判断是否需要缓存该子组件。

useMemo/useCallback使用准则:
1.大部分的 useMemo 和 useCallback 都应该移除,他们可能没有带来任何性能上的优化,反而增加了程序首次渲染的负担,并增加程序的复杂性。
2.使用 useMemo 和 useCallback 优化子组件 re-render 时,必须同时满足以下条件才有效。
子组件已通过 React.memo 或 useMemo 被缓存
子组件所有的 prop 都被缓存

3.不推荐默认给所有组件都使用缓存,大量组件初始化时被缓存,可能导致过多的内存消耗,并影响程序初始化渲染的速度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪里个浪里个浪里个浪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值