useEffect Hook Example: What causes the re-render?

Xen*_*mar 10 reactjs react-hooks

I am trying to figure out, when useEffect causes a rerender. I am very surprised by the result of the following example:

https://codesandbox.io/embed/romantic-sun-j5i4m

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" />;
}
Run Code Online (Sandbox Code Playgroud)

The result of this example is as follows:

在此处输入图片说明

My confusion stems from two things: I don't know why:

  1. The component renders only three times (I would have guessed the component would rerender for every call to setCount + one initial render - so 4 times)
  2. The counter only ever has two values 0 and 3: I guess, as this article states, every render sees its own state and props so the entire loop will be run with each state as a constant (1, 2, 3) --> But why is the state never 2?

I would be super happy if someone could clarify. Thanks!

App*_*son 7

我将尽力解释(或逐步进行)正在发生的事情。我还在第7点和第10点做两个假设。

  1. 应用程序组件安装。
  2. useEffect 安装后称为。
  3. useEffect将“保存”初始状态,因此counter每次在其内部引用时将为0。
  4. 循环运行3次。每次迭代setCount都会调用以更新计数,并且控制台日志会记录计数器,根据“存储的”版本,该计数器为0。因此,数字0在控制台中记录了3次。因为状态已更改(0-> 1、1-> 2、2-> 3),所以React集就像一个标志或告诉自己要记住要重新渲染的东西。
  5. 在执行期间,React尚未重新渲染任何内容useEffect,而是等到useEffect完成之后再渲染。
  6. 一旦useEffect完成,React就会记住的状态counter在其执行过程中已更改,因此它将重新呈现该App。
  7. 该应用程序重新渲染,然后useCounter再次被调用。请注意,这里没有参数传递给useCounter自定义钩子。 假设: 我自己也不知道这一点,但是我认为默认参数似乎又被创建了,或者至少以某种方式使React认为它是新的。因此,由于将arr视为新的,因此该useEffect钩子将再次运行。这是我可以useEffect再次解释运行的唯一原因。
  8. 在的第二次运行期间useEffect,的counter值将为3。因此,控制台日志将记录3号为预期的三倍。
  9. useEffect第二次运行后,React发现计数器在执行过程中发生了变化(3-> 1,1-> 2,2-> 3),因此该应用将重新渲染,从而导致第三个“渲染”日志。
  10. 假设: 因为useCounter从App的角度来看,挂钩的内部状态在此渲染与前一个渲染之间没有发生变化,所以它不会在其中执行代码,因此useEffect不会被第三次调用。因此,应用程序的第一个渲染将始终运行挂钩代码。应用程序第二次看到钩子的内部状态counter从0 更改为3,因此决定重新运行它,第三次应用程序看到钩子的内部状态为3并且仍然为3,因此决定不重新执行-运行。这是我想出的让钩子不再运行的最好原因。您可以在挂钩本身中放入一个日志,以查看它实际上并没有第三次运行。

这就是我所看到的,我希望这可以使它更加清晰。


Xen*_*mar 5

我在react文档中找到了第三个渲染的解释。我认为这澄清了为什么在不应用效果的情况下做出第三次渲染的反应:

如果您将State Hook更新为与当前状态相同的值,React将在不渲染子级或激发效果的情况下纾困。(React使用Object.is比较算法。)

请注意,React可能仍需要在渲染之前再次渲染该特定组件。不必担心,因为React不会不必要地“深入”到树中。如果渲染时要进行昂贵的计算,则可以使用useMemo优化它们。

看来useState和useReducer共享了这种纾困逻辑。