Reactjs useEffect钩子示例:是什么导致重新渲染?

Reactjs useEffect钩子示例:是什么导致重新渲染?,reactjs,react-hooks,Reactjs,React Hooks,我想弄清楚,useEffect什么时候会导致重播。我对以下示例的结果感到非常惊讶: 函数useCounter(arr=[1,2,3]){ const[counter,setCount]=useState(0); useffect(()=>{ 用于(arr的常数i){ 设置计数(i); 控制台日志(计数器); } },[arr]); } 函数App(){ 使用计数器(); 控制台日志(“呈现”); 返回; } 此示例的结果如下所示: 我的困惑源于两件事:我不知道为什么: 该组件只渲染三次(我

我想弄清楚,useEffect什么时候会导致重播。我对以下示例的结果感到非常惊讶:

函数useCounter(arr=[1,2,3]){
const[counter,setCount]=useState(0);
useffect(()=>{
用于(arr的常数i){
设置计数(i);
控制台日志(计数器);
}
},[arr]);
}
函数App(){
使用计数器();
控制台日志(“呈现”);
返回;
}
此示例的结果如下所示:

我的困惑源于两件事:我不知道为什么:

  • 该组件只渲染三次(我猜该组件会为每次调用setCount+一次初始渲染重新渲染,所以会渲染4次)
  • 计数器只有两个值0和3:我想,当这个值为0和3时,每个渲染都会看到自己的状态和道具,因此整个循环将以每个状态作为常量(1,2,3)运行-->但是为什么状态永远不会为2
  • 如果有人能澄清,我会非常高兴。谢谢

    和类似的钩子不会立即重新加载组件。他们可能会批处理更新或将更新推迟到以后。因此,在使用
    计数器===3的最新
    setCount
    之后,您只会得到一个重新渲染器

    使用
    计数器===0
    获得初始渲染,使用
    计数器===3
    获得两个额外的重新渲染。我不知道为什么它不会进入无限循环
    arr=[1,2,3]
    应在每次调用时创建一个新数组,并触发
    useffect

  • 初始渲染集
    计数器
    0
  • useffect
    记录
    0
    三次,将
    计数器
    设置为
    3
    并触发重新渲染
  • 计数器===3
  • useffect
    记录
    3
    三次,将
    计数器设置为
    3

  • React应该在这里停止,或者从第3步转到无限循环。

    我将尽最大努力解释(或遍历)正在发生的事情。我还在第7点和第10点做两个假设

  • 应用程序组件挂载
  • 装入后调用
    useffect
  • useffect
    将“保存”初始状态,因此每当在其内部引用时,
    计数器将为0
  • 循环运行3次。每次迭代
    setCount
    都会被调用以更新计数,控制台日志会记录计数器,根据“存储”版本,计数器为0。因此,数字0在控制台中记录了3次。因为状态已更改(0->1,1->2,2->3),所以会像一个标志或其他东西一样对集合进行反应,以提醒自己记住重新渲染
  • React在执行
    useffect
    期间未重新渲染任何内容,而是等待
    useffect
    完成后重新渲染
  • 一旦
    useffect
    完成,React会记住
    计数器的状态在执行过程中发生了变化,因此它将重新呈现应用程序
  • 应用程序重新呈现,并再次调用
    useCounter
    。请注意,没有任何参数被传递到
    useCounter
    自定义挂钩。 Asumption:我自己也不知道这一点,但我认为默认参数似乎又被创建了,或者至少在某种程度上使React认为它是新的。因此,由于
    arr
    被视为新的,因此
    useffect
    钩子将再次运行。这是我能够解释第二次运行
    useffect
    的唯一原因
  • 在第二次运行
    useffect
    期间,
    计数器的值为3。因此,控制台日志将按预期记录数字3三次
  • 在第二次运行
    useffect
    后,React发现计数器在执行过程中发生了更改(3->1,1->2,2->3),因此应用程序将重新渲染,导致出现第三个“渲染”日志
  • Asumption:因为从应用程序的角度来看,
    useCounter
    钩子的内部状态在本次渲染和上一次渲染之间没有改变,所以它不会在其内部执行代码,因此不会第三次调用
    useffect
    。因此,应用程序的第一次渲染将始终运行钩子代码。第二次应用程序看到钩子的内部状态从0更改为3,因此决定重新运行钩子,第三次应用程序看到内部状态是3,现在仍然是3,因此决定不重新运行钩子。这是我能想出的钩子不再跑的最好理由。您可以将日志放在钩子本身中,以查看它实际上不会第三次运行
  • 这就是我看到的情况,我希望这能让它更清晰一点。

    我在react文档中找到了第三次渲染的一个。我认为这澄清了为什么第三个渲染没有应用效果:

    如果将状态挂钩更新为与当前状态相同的值, React将在不渲染儿童或射击效果的情况下跳出。 (React使用Object.is比较算法。)

    请注意,React可能仍然需要再次渲染该特定组件 在救援之前。这不应该是一个问题,因为反应不会 不必要地“深入”树中。如果你做的很昂贵 渲染时,可以使用UseMoom优化计算


    似乎useState和useReducer都有这种纾困逻辑。

    有一个巧合,可能会在原始版本中造成一些混乱。主要是有3个渲染,并且
    useCounter
    的默认参数长度等于3。下面您可以看到,即使对于更大的数组,也只有3个渲染

    函数useCounter(arr=[1,2,3,4,5,6]){
    常量[计数器,设置计数]=React.useState(0);
    React.useffect(()=>{
    用于(arr的常数i){
    设置计数(i);
    
    function useCounter(arr = [1, 2, 3]) {
      const [counter, setCount] = useState(0);
      useEffect(() => {
        for (const i of arr) {
          setCount(i);
          console.log(counter);
        }
      }, [arr]);
    }
    
    function App() {
      useCounter();
      console.log("render");
      return <div className="App" />;
    }
    
    import React, { useEffect, useState } from "react";
    import ReactDOM from "react-dom";
    
    import "./styles.css";
    
    const defaultVal = [1, 2, 3];
    
    function useCounter(arr = defaultVal) {
      const [counter, setCount] = useState(0);
    
      useEffect(() => {
        console.log(counter);
        setCount(arr);
      }, [counter, arr]);
    
      return counter;
    }
    
    function App() {
      const counter = useCounter();
      console.log("render");
      return (
        <div className="App">
          <div>{counter}</div>
        </div>
      );
    }
    
    const rootElement = document.getElementById("root");
    
    ReactDOM.render(<App />, rootElement);