当前位置: 首页 > 知识库问答 >
问题:

为什么使用useMemo()钩子的React函数组件调用的备忘录化不起作用?

谢英光
2023-03-14

好的,我想我最好在这里补充一点,我知道代码看起来不太好,但我认为它会起作用,所以我很高兴你能解释为什么它不仅有效,而且无效。我也很高兴听到你对这个问题的解决办法!

我很难理解为什么下面的代码块似乎没有按照预期工作,我的意思是记忆

顺便说一句,如果你能解释为什么渲染,我也会很感激

非常感谢你的帮助!

import "./styles.css";
import React from "react";

const First = () => {
  const renders = React.useRef(0);
  renders.current += 1;
  console.log('<First /> renders: ', renders.current);

  return <h1>First</h1>;
}

const Second = () => {
  const renders = React.useRef(0);
  renders.current += 1;
  console.log('<Second /> renders: ', renders.current);

  return <h1>Second</h1>;
}

const App = () => {
  const [isSwapped, setIsSwapped] = React.useState(false);
  const [, dummyState] = React.useState(false);

  const first = React.useMemo(() => <First />, []);
  const second = React.useMemo(() => <Second />, []);

  const renders = React.useRef(0);
  renders.current += 1;
  console.log('<App /> renders: ', renders.current);

  return (
    <div className="App">
      <button onClick={() => setIsSwapped((isSwapped) => !isSwapped)}>Swap</button>
      <button onClick={() => dummyState((state) => !state)}>Re-render</button>
      {isSwapped ? (
        <>
          {first}
          {second}
        </>
      ) : (
        <>
          {second}
          {first}
        </>
      )}
    </div>
  );
}

编辑:感谢回复,伙计们,这是一个版本,做什么是打算:https://codesandbox.io/s/why-doesnt-memoization-of-a-react-functional-component-call-with-usememo-hook-forked-gvnn0?file=/src/App.js


共有2个答案

卢光远
2023-03-14

我用组件的返回值通过usemohook进行了记忆,然后用React对每个组件进行了包装。备忘录,结果对我来说似乎是一样的。

使用“备注高阶”组件装饰组件将对渲染结果进行备注。

如果您的组件在给定相同的道具的情况下呈现相同的结果,您可以将其包装在对React.memo的调用中,以便在某些情况下通过记住结果来提高性能。这意味着React将跳过渲染组件,并重用上次渲染的结果。

const First = React.memo(() => {
  const renders = React.useRef(0);

  React.useEffect(() => {
    renders.current += 1;
    console.log("<First /> renders: ", renders.current);
  });

  return <h1>First</h1>;
});

const Second = React.memo(() => {
  const renders = React.useRef(0);

  React.useEffect(() => {
    renders.current += 1;
    console.log("<Second /> renders: ", renders.current);
  });

  return <h1>Second</h1>;
});

你会发现我在下面的问题中也提到了无意的副作用(控制台日志和ref突变):

另一方面,如果您能帮助我理解为什么父组件似乎要将2添加到渲染中,那也会很酷。即使我放了一个控制台,每次渲染都会显示当前的。log('render')在组件中,它只显示一次。

这是因为组件生命周期有两个阶段,“呈现阶段”和“提交阶段”。

请注意,“render”方法发生在“render阶段”,并且回想一下,功能组件的整个组件体就是“render方法”。这意味着React可以并且将可能多次呈现组件,以便计算在“提交阶段”需要呈现给DOM的内容的差异。“提交阶段”传统上被认为是(向DOM)呈现组件。还请注意,此阶段是可以运行副作用的阶段,即useffect

将日志和ref更新放置在useffecthook中,以便在每个渲染周期运行它们一次。

function App() {
  const [isSwapped, setIsSwapped] = React.useState(false);
  const [, dummyState] = React.useState(false);

  const renders = React.useRef(0);

  React.useEffect(() => {
    renders.current += 1;
    console.log("<App /> renders: ", renders.current);
  });

  return (
    <div className="App">
      <button onClick={() => setIsSwapped((isSwapped) => !isSwapped)}>
        Swap
      </button>
      <button onClick={() => dummyState((state) => !state)}>Re-render</button>
      {isSwapped ? (
        <>
          <First />
          <Second />
        </>
      ) : (
        <>
          <Second />
          <First />
        </>
      )}
    </div>
  );
}

暴德运
2023-03-14

关于问题的第二部分,关于渲染的价值。current可能会更新两次而不是一次,因为您正在使用React。在你的索引中输入模式。jsx文件。删除StrictMode似乎解决了本例中的问题。请查看此链接以了解更多信息。

对于第一个问题,似乎在单击“重新渲染”按钮时,子组件(第一个或第二个)中的任何一个都会重新渲染。然而,当我们交换它们时,它们不仅会重新渲染,而且还会卸载组件,然后重新装载。您也可以在上面的示例中看到这种行为。为这两个组件提供唯一的键似乎可以解决这个问题。我也是React的初学者,并不完全确定幕后发生了什么,但这是我根据文档收集到的:当React发现一个子(比如第一个子)的类型与上一次渲染时的类型不同(在我们的例子中,类型在第一个和第二个之间切换)它卸载子节点及其所有子节点,然后重新装载它们,然后继续渲染过程。在上面的示例中,我们可以看到控制台日志支持这一点。通过包含unqiue键,我们让react知道,尽管第一个和第二个组件位于不同的位置,但它们毕竟是同一个组件。因此,react不会遇到子级交换类型的场景。

 类似资料:
  • 我正在重构我们的一些组件,因此我尝试合并记忆,因为一些组件可能会使用相同的值重新呈现(例如,热链接图像URL,除非它们相同)。 我有一个简单的组件: 然后是阿凡达笠美: 我还试着传递备忘录的第二个论点: 但是console.log仍然每次都显示出来。我显然遗漏了什么,或者不太明白这是怎么回事。这个组件是几个级别下来,但它通过在img如果它是每次可用的,所以我希望它知道,如果img是通过在以前的渲染

  • 问题:在函数“selectmenu”中调用了React钩子“React.useEffect”,该函数既不是React函数组件,也不是自定义React钩子函数 目标:我只想在单击按钮时挂载组件('Component DidMount/WillUnmount)(使用useffect()),而不是在加载文件(或整个组件)时挂载 实际目标:我想在单击时选择(或突出显示)一个文件(自定义)。但是,当用户在文

  • 我试图在一个功能组件中调用一个API,它是一个反应挂钩,并基于结果,重新呈现组件的内容。下面是代码: 调用API的组件- 以下是功能: 我正在尝试获取状态数据,并根据数据重新渲染组件。这里处于调用外部API的操作中。 正在调用操作并成功获取数据,但我不确定为什么状态正在更新-我得到了以下错误- 在函数“setResults”中调用React钩子“useState”,该函数既不是React函数组件,

  • 问题内容: 说我有一个功能组件: 直接将其作为函数调用有什么区别: 与将其称为组件相比: 我对性能影响最感兴趣,React如何在内部对它们进行区别对待,也许对React Fiber中的情况可能有所不同,我听说功能组件的性能得到了提升。 问题答案: 调用它作为函数要快得多,事实上,几个月前就已经有讨论了。在这一点上,功能性反应组件不能是PureComponents,因此没有真正适用于它们的额外优化。

  • 暴力搜索 遍历所有子集 代码 imhuay/AlgorithmforInterview/_utils工具函数/遍历所有子集.hpp 相关问题 遍历所有正因子 代码 imhuay/AlgorithmforInterview/_utils工具函数/遍历所有正因子.hpp 相关问题 二分查找 离散模板 连续模板 位运算 代码 imhuay/AlgorithmforInterview/_utils工具函数

  • 问题内容: 我刚开始使用Python,却不知道什么是记忆以及如何使用它。另外,我可以举一个简化的例子吗? 问题答案: 记忆有效地指基于方法输入记忆方法调用的结果(“记忆”→“备忘录”→要记忆),然后返回记忆的结果,而不是再次计算结果。你可以将其视为方法结果的缓存。有关更多详细信息,请参见第387页的Cormen等人的《算法简介》(3e)中的定义。 一个简单的示例,使用Python中的记忆来计算阶乘