首页 > 解决方案 > 生成器在 React 中的调用次数超出预期

问题描述

我发现生成器似乎被调用了两次的行为。

下面是一个简单的代码,它从生成器中获取一个数字并将其输出到控制台。它期望 0 和 1 输出到控制台,但实际上它输出的是 0 和 2。

import { useState, useEffect } from "react";
 
function* counter() {
  let val = 0;
  while (true) yield val++;
}
const count = counter();

function App() {
  console.log("rendered: count = ", count.next().value);
  const [hoge, setHoge] = useState("first");
  console.log("rendered:", hoge);

  useEffect(() => setHoge("second"), [setHoge]);
  return <div>{hoge}</div>;
}

export default App;

演示: https ://codesandbox.io/s/friendly-http-g84cp?file=/src/App.tsx

不仅如此useEffect,我还发现与setInterval. 此外,如果我们删除 <React.strict>,控制台会按预期输出 0 和 1。

您知道为什么会发生这种行为吗?

标签: javascriptreactjs

解决方案


根据文档

严格模式不能自动为您检测副作用,但它可以通过使它们更具确定性来帮助您发现它们。这是通过有意双重调用以下函数来完成的:

  • 类组件构造函数、render 和 shouldComponentUpdate 方法
  • 类组件静态 getDerivedStateFromProps 方法
  • 功能组件体
  • 状态更新函数(setState 的第一个参数)
  • 传递给 useState、useMemo 或 useReducer 的函数

函数组件体被调用两次是您的问题的原因。这意味着在初始挂载时,App函数体将被调用两次,然后在设置新状态时,App函数体将被调用两次。按照这个逻辑,您应该已经看到了另外 4 个日志,即总共 8 个。

这是以下异常出现的地方:-

从 React 17 开始,React 会自动修改 console.log() 等控制台方法,以在对生命周期函数的第二次调用中静默日志。但是,在某些可以使用变通方法的情况下,它可能会导致不良行为。

要实际可视化所有 8 个日志,您可以let log = console.log在顶层执行并替换console.logby 的使用log,您将看到实际发生的情况。

简单地说,不要将这样的行为放在函数体中,因为这是严格模式应运而生的副作用。

这是一个分叉的代码框,可以看到:-

编辑 dreamy-aryabhata-ddzyz


推荐阅读