简体   繁体   English

这是对 useCallback 和 useMemo 的错误使用吗?

[英]Is this incorrect use of useCallback and useMemo?

When should we worry about functional components redefining variables and functions??我们什么时候应该担心函数组件重新定义变量和函数?

I think this first case is clearly unnecessary, functions is not expensive to create(and the react dev tools profile test didn't detect performace change with useCallback)我认为第一种情况显然是不必要的,创建函数并不昂贵(并且反应开发工具配置文件测试没有检测到 useCallback 的性能变化)

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

export const App = () => {
  const [counter, setCounter] = useState(1);

  const showCounter = useCallback(() => {
    console.log(counter);
  }, [counter]);

  return (
    <>
      <button onClick={showCounter}>Show</button>
      <button onClick={() => setCounter(counter + 1)}>Add</button>
    </>
  );
};

In the other hand, in this other exemple, with react dev tools we can detect that useMemo cuts in half the time of rendering:另一方面,在另一个示例中,使用 react 开发工具,我们可以检测到 useMemo 将渲染时间缩短了一半:

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

export const App = () => {
  const [counter, setCounter] = useState(1);

  const [list, setList] = useState<number[]>([]);

  function setListItem(item: number) {
    setList([...list, item]);
  }

  //should we useMemo here?
  const domList = useMemo(
    () =>
      list.map((value: number, index: number) => (
        <li key={index}> {value} </li>
      )),
    [list]
  );

  return (
    <>
      <p>{counter}</p>
      <button onClick={() => setCounter(counter - 1)}>Subtract</button>
      <button onClick={() => setCounter(counter + 1)}>Add</button>
      <button onClick={() => setListItem(counter)}>Push to array</button>
      <h1>List</h1>
      <ul>{domList}</ul>
    </>
  );
};

But still less than a millisecond of gain and array maps and JSX array are not something really expensive when we talk about front-end either.但是当我们谈论前端时,仍然不到一毫秒的增益和数组映射和 JSX 数组也不是很昂贵的东西。 However, leaving an array map method run in each render is very uncomfortable to see.但是,在每个渲染中运行一个数组 map 方法是很不舒服的。 So, what of those things should we do?那么,这些事情我们应该怎么做呢? (Let's forget about when we have useEffect that depends of some variables that make the useMemo necessary) (让我们忘记什么时候我们有 useEffect 依赖于一些使 useMemo 成为必要的变量)

  • useCallback and useMemo in all functions and variables that don't need to be redefined in each render.所有不需要在每次渲染中重新定义的函数和变量中的 useCallback 和 useMemo。
  • useCallback and useMemo only in things that we can clearly see a performance gain(even if most time would not count as an improvement to UX). useCallback 和 useMemo 仅用于我们可以清楚地看到性能提升的事情(即使大多数时间都不能算作对 UX 的改进)。 So we would need useMemo in this second example.所以在第二个例子中我们需要 useMemo。
  • useCallback and useMemo only when not using it in a specific case we would get a great UX decline.仅当在特定情况下不使用 useCallback 和 useMemo 时,我们会得到很大的用户体验下降。 So only really expensive functions would useCallback所以只有真正昂贵的函数才会使用回调

The performance you gain with each of them is different.您获得的性能是不同的。 Yes, for useMemo if the calculation you are memoizing is expensive, it offers a clear immediate benefit.是的,对于useMemo如果您正在记忆的计算很昂贵,它提供了明显的直接好处。 However, for useCallback and useMemo , the long-term benefits refer to avoiding unnecessary renders in child components , not the recalculation cost.但是,对于useCallbackuseMemo ,长期的好处是避免了子组件中不必要的渲染,而不是重新计算的成本。

Remember, if any of the props in a component change, they may trigger a render call, and the higher in the component tree this is the case, the more it will affect performance.请记住,如果组件中的任何道具发生变化,它们可能会触发渲染调用,并且在组件树中越靠前,它对性能的影响就越大。 That is the reason we normally use those hooks.这就是我们通常使用这些钩子的原因。

In general, components have several state elements, and with memo hooks, we can establish dependencies to avoid computational redundancy.一般来说,组件有几个 state 元素,通过备忘录挂钩,我们可以建立依赖关系以避免计算冗余。 For instance, in your synthetic case, you have two state elements: counter , and list .例如,在您的合成案例中,您有两个 state 元素: counterlist Each time counter changes, you prevented domList to be recalculated by making it dependent only on list .每次counter更改时,您都会通过使其仅依赖于list来防止重新计算domList If this was a leaf node, sure no performance gain, however, this is the root of your component tree, and without those hooks, performance will not scale as you add more state elements and you will have to refactor your code to address performance.如果这是一个叶节点,那么肯定没有性能提升,但是,这是组件树的根,如果没有这些钩子,性能将不会随着您添加更多 state 元素而扩展,您将不得不重构代码以解决性能问题。 Sure, <ul>{domList}</ul> can be costly if the list has more than 1000 elements, but <ul><ExpensiveCutie domList={domList} /></ul> can take you weeks to notice, debug, and refactor.当然,如果列表包含超过 1000 个元素, <ul>{domList}</ul>的成本可能会很高,但<ul><ExpensiveCutie domList={domList} /></ul>可能需要数周时间才能注意到、调试、和重构。

In short, I recommend using memo hooks in container components and not in leaf components.简而言之,我建议在容器组件中使用备忘录挂钩,而不是在叶组件中。 Yet, what if your leaf component becomes a container as you are implementing it?然而,如果你的叶子组件在你实现它的时候变成了一个容器呢?

Before you go, do you even need to use memo()?在你 go 之前,你甚至需要使用 memo() 吗? Read more here .在这里阅读更多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM