1

我们什么时候应该担心函数组件重新定义变量和函数?

我认为第一种情况显然是不必要的,创建函数并不昂贵(并且反应开发工具配置文件测试没有检测到 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>
    </>
  );
};

另一方面,在另一个示例中,使用 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>
    </>
  );
};

但是当我们谈论前端时,仍然不到一毫秒的增益和数组映射和 JSX 数组也不是什么真正昂贵的东西。然而,在每次渲染中运行一个数组映射方法是非常不舒服的。那么,这些事情我们应该怎么做呢?(让我们忘记什么时候我们有 useEffect 依赖于一些使 useMemo 成为必要的变量)

  • 所有不需要在每次渲染中重新定义的函数和变量中的 useCallback 和 useMemo。
  • useCallback 和 useMemo 仅用于我们可以清楚地看到性能提升的事情(即使大多数时间都不能算作对 UX 的改进)。所以在第二个例子中我们需要 useMemo。
  • 仅当在特定情况下不使用 useCallback 和 useMemo 时,我们会得到很大的用户体验下降。所以只有真正昂贵的函数才会使用回调
4

1 回答 1

1

您获得的性能是不同的。是的,因为useMemo如果您要记忆的计算很昂贵,它会提供明显的直接好处。但是,对于useCallbackuseMemo,长期的好处是避免在子组件中进行不必要的渲染,而不是重新计算成本。

请记住,如果组件中的任何道具发生变化,它们可能会触发渲染调用,并且在组件树中越靠前,它对性能的影响就越大。这就是我们通常使用这些钩子的原因。

一般来说,组件有几个状态元素,通过备忘录钩子,我们可以建立依赖关系以避免计算冗余。例如,在您的合成案例中,您有两个状态元素:counterlist。每次counter更改时,您domList都会通过使其仅依赖于来防止重新计算list。如果这是一个叶节点,那么肯定没有性能提升,但是,这是组件树的根,如果没有这些钩子,性能将不会随着您添加更多状态元素而扩展,您将不得不重构代码以解决性能问题。当然, <ul>{domList}</ul>如果列表包含 1000 多个元素,成本可能会很高,但<ul><ExpensiveCutie domList={domList} /></ul>可能需要数周时间来注意、调试和重构。

简而言之,我建议在容器组件中使用备忘录挂钩,而不是在叶组件中。然而,如果你的叶子组件在你实现它的时候变成了一个容器呢?

在你走之前,你甚至需要使用 memo() 吗?在这里阅读更多。

于 2021-03-06T20:47:41.707 回答