Reactjs useEffect钩子示例:是什么导致重新渲染?
我想弄清楚,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(){ 使用计数器(); 控制台日志(“呈现”); 返回; } 此示例的结果如下所示: 我的困惑源于两件事:我不知道为什么: 该组件只渲染三次(我
函数useCounter(arr=[1,2,3]){
const[counter,setCount]=useState(0);
useffect(()=>{
用于(arr的常数i){
设置计数(i);
控制台日志(计数器);
}
},[arr]);
}
函数App(){
使用计数器();
控制台日志(“呈现”);
返回;
}
此示例的结果如下所示:
我的困惑源于两件事:我不知道为什么:
计数器===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);