Javascript 在React中调用生成器的次数超出预期

Javascript 在React中调用生成器的次数超出预期,javascript,reactjs,Javascript,Reactjs,我发现一个行为,生成器似乎被调用了两次 下面是一个简单的代码,它从生成器获取一个数字并将其输出到控制台。 它期望0和1输出到控制台,但实际上它输出0和2 import { useState, useEffect } from "react"; function* counter() { let val = 0; while (true) yield val++; } const count = counter(); function App() { cons

我发现一个行为,生成器似乎被调用了两次

下面是一个简单的代码,它从生成器获取一个数字并将其输出到控制台。 它期望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;
从“react”导入{useState,useffect};
函数*计数器(){
设val=0;
而(true)产生val++;
}
常数计数=计数器();
函数App(){
log(“呈现:count=,count.next().value”);
const[hoge,setHoge]=useState(“第一”);
console.log(“呈现:”,hoge);
使用效果(()=>setHoge(“第二”),[setHoge]);
返回{hoge};
}
导出默认应用程序;
演示:

不仅对
useffect
,我还发现了与
setInterval
相同的行为。此外,如果我们删除,控制台将按预期输出0和1

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

根据

严格模式无法自动检测副作用,但它可以通过使副作用更具确定性来帮助您发现副作用。这是通过故意双重调用以下函数来实现的:

  • 类组件构造函数、呈现和shouldComponentUpdate方法
  • 类组件静态getDerivedStateFromProps方法
  • 功能组件体
  • 状态更新程序函数(setState的第一个参数)
  • 传递给useState、UseMoom或useReducer的函数
函数组件体被调用两次是问题的原因。这意味着在初始装载时,
App
函数体将被调用两次,然后在设置新状态时,
App
函数体将被调用两次。按照这种逻辑,您应该已经看到了4个以上的日志,即总共8个

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

从React 17开始,React自动修改控制台 方法(如console.log())在第二次调用 生命周期功能。但是,它可能会导致不希望的行为 在某些情况下,可以使用变通方法

要真正可视化所有8个日志,您只需在顶层执行
let log=console.log
,并将
console.log
的用法替换为
log
,您将看到实际发生的情况

简单地说,不要将这种行为放在功能体中,因为这是严格模式产生的副作用

下面是一个分叉代码沙盒,可以看到:-

存在用于精确检测代码中的反模式操作(渲染函数中的副作用)

此模式故意执行组件的生命周期方法两次(在您的情况下,它是一个功能组件,因此功能执行两次),同时吞下
控制台.log


这就是为什么在控制台中没有任何日志的情况下观察生成器增量。

为什么将
setHoge
放在
useffect
的依赖项数组中?应该是
hoge
这不会导致无限的更新,因为每次更新hoge时,useffect都会更新hoge?不,这不会导致无限的更新
useffect
检查状态变量而不是状态更新程序函数(
setHoge
)。感谢您的详细解释!我现在明白了原因:严格模式是故意读取应用程序两次以检测副作用。这很有趣。副作用是由计数器作为生成器的实现引起的。我没有注意到这一点,尽管现在已经很明显了。非常感谢你!