💡我居然用错了useMemo和useCallback这么久?

简介: 💡我居然用错了useMemo和useCallback这么久?

我们知道,useMemouseCallback主要作用是缓存中间状态,减少无意义的的render从而提高性能。但是最近我发现我对它们的使用一直有误解!

💎 对useMemo的误解

请看下面的代码,即使用了useMemo,在isZero的没有变的情况下,第二个子组件还是重新渲染了!

import { useCallback, useMemo, useState } from "react";
const Child = ({ value, onClick }) => {
  return (
    <div
      style={{
        height: 100,
        background: `#${(~~(Math.random() * (1 << 24))).toString(16)}`
      }}
    >
      my value is {value.toString()}
    </div>
  );
};
export default function App() {
  const [count, setCount] = useState(0);
  const isZero = useMemo(() => !!(count % 3), [count]);
  const onClick = useCallback(() => setCount(count + 1), [count]);
  return (
    <div className="App">
      <button onClick={onClick}>click me</button>
      <Child value={count} />
      <Child value={isZero} />
    </div>
  );
}

💡相关阅读

🎉尤雨溪为什么要推出Vapor Mode🎉

其实原因在之前的文章中也提到过:

React每次当组件状态发生改变时,都会从当前组件开始一直到所有叶子节点组件重新渲染。

文中同时也提到了这个问题的解决方案:子组件使用memo函数包裹,组件就可以按预期渲染了。

但是,此时我们去掉useMemo,子组件依然是按期望渲染的。

memouseMemo类似,都是基于Object.is的浅比较,仅仅对非引用类型有效。

所以上面的示例中,使用useMemo是没有意义的。

💎 对useCallback的误解

然而,上面的示例中,即使onClick函数不使用useCallback,组件也会按预期渲染。这是因为不管onClick的回调函数的缓存是否发生改变,App组件注定都会被渲染。

所以,现在我们得到了一个合理的代码,如下:

import { memo, useCallback, useMemo, useState } from "react";
const Child = memo(({ value, onClick }) => {
  return (
    <div
      style={{
        height: 100,
        background: `#${(~~(Math.random() * (1 << 24))).toString(16)}`
      }}
    >
      my value is {value.toString()}
    </div>
  );
});
export default function App() {
  const [count, setCount] = useState(0);
  // const isZero = useMemo(() => !!(count % 3), [count]);
  const isZero = !!(count % 3);
  // const onClick = useCallback(() => setCount(count + 1), [count]);
  const onClick = () => setCount(count + 1);
  return (
    <div className="App">
      <button onClick={onClick}>click me</button>
      <Child value={count} />
      <Child value={isZero} />
    </div>
  );
}

那到底应该何时使用useCallback呢?

请看下面的例子。在上面的代码基础上添加如下代码:

const onClickMethod = () => console.log("lll");
  return (
    <div className="App">
      <button onClick={onClick}>click me</button>
      <Child value={count} onClick={onClickMethod} />
      <Child value={isZero} onClick={onClickMethod} />
    </div>
  );

此时,发现组件无法按预期渲染了,不管isZero是否发生变化,第二个Child组件都会被重新渲染。

这是因为此时的onClickMethod方法被做为Child组件的onClick属性了。

如果现在将onClickMethod方法使用useCallback包裹起来,就又正常了。

const onClickMethod = useCallback(() => console.log("lll"), []);

这才是useCallback的正确用法!

💎 总结

我们在写组件时,应该遵循下面的规律,可以有效提高页面性能:

  • 👉尽量多用memo方法包裹组件(减少渲染次数)
  • 👉当子组件的属性为非引用类型的中间状态时请用useMemo(减少渲染次数)
  • 👉当子组件的属性为函数时请用useCallback(减少渲染次数)
  • 👉仅作用在当前组件范围内的属性,尽量不要使用useMemouseCallback(减少调用)

好了今天的分享到这了,希望你也不要跟我一样再用错useMemouseCallback了!

相关文章
|
3月前
|
前端开发
react18【系列实用教程】Hooks 闭包陷阱 (2024最新版)含useState 闭包陷阱,useEffect 闭包陷阱,useCallback 闭包陷阱
react18【系列实用教程】Hooks 闭包陷阱 (2024最新版)含useState 闭包陷阱,useEffect 闭包陷阱,useCallback 闭包陷阱
57 0
|
5月前
|
前端开发
掌握React中的useCallback:优化性能的秘诀
掌握React中的useCallback:优化性能的秘诀
|
5月前
|
存储 缓存 前端开发
我知道你想用useEffect,但你先别急
useEffect是React提供给我们的一个“逃生舱”,是React 的纯函数式世界通往命令式世界的“逃生通道”,选择合适的时机使用useEffect会让我们的代码既优雅又高效,反之会造成不必要的负担。
|
存储 缓存 前端开发
React中useMemo和useCallback如何做到性能优化?
React中useMemo和useCallback如何做到性能优化?
|
5月前
|
前端开发 JavaScript 定位技术
蜕变之始,useEffect 最后一种用法
蜕变之始,useEffect 最后一种用法
|
12月前
|
缓存 前端开发
🤔useMemo还可以这样用?useCallback:糟了,我成替身了!
🤔useMemo还可以这样用?useCallback:糟了,我成替身了!
|
12月前
|
存储 前端开发 JavaScript
🤮是时候放弃useState了,🚀这么写React更丝滑🚀
🤮是时候放弃useState了,🚀这么写React更丝滑🚀
|
存储 前端开发 JavaScript
面试官:useEffect和useLayoutEffect有什么区别?
源码角度剖析useEffect和useLayoutEffect区别
146 0
面试官:useEffect和useLayoutEffect有什么区别?
|
前端开发
浅尝一颗语法糖 async / await
这是 ES7 提供的语法糖,是真的很友好!
67 0
|
缓存 前端开发
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-usememo和memo得区别
前端学习笔记202307学习笔记第五十七天-模拟面试笔记react-usememo和memo得区别
36 0