调用 setState 时会发生什么?
What happens when setState is invoked?
我正在学习 React,但很难理解正在发生的代码流。
这是我的 code:It 是一个功能性的 React 组件
function App() {
const [x, setx] = useState(1);
const [x1, setx1] = useState(1);
const [x2, setx2] = useState(1);
const [x3, setx3] = useState(1);
const [x4, setx4] = useState(1);
const [x5, setx5] = useState(1);
console.log("out3");
const bclick = () => {
setx1(x1 + 1);
setx2(x2 + 1);
setx3(x3 + 1);
setx4(x4 + 1);
setx5(x5 + 1);
setx(x + 1);
console.log("out1");
bclick2();
};
const bclick2 = () => {
console.log("out2");
};
console.log("out4");
return (
<div className="App">
{console.log("in")}
<button onClick={bclick} />
</div>
);
}
点击按钮后 console.log() 的输出:
输出1
输出2
输出3
输出4
在
Q> 单击按钮后,将执行多个不同的 setState。他们会重新评估组件或功能链(bclick 和 bclick2)完成执行,然后重新评估 App 组件。
根据我的输出,我意识到首先执行函数链。
那么 setState 是这样工作的吗?
代码流是否会先完成(不管函数的数量)然后重新评估功能组件?
这与 React 批处理 setState
调用有关,以优化渲染数量。通常你不必担心。
setState
调用是 async
。 React 将决定何时以及如何应用多个 setState
调用。
处理程序将始终在 React re-renders 之前完成 运行ning。这就是为什么您会在任何 re-renders.
之前看到 bclick2()
调用 运行ning
我觉得 React 总是会在单个 re-render 中批处理多个 setState
调用。但是你可以看到,如果你在 setTimeout
中包装多个 setState
调用,React 将 re-render 多次,因为它无法知道这些超时需要多长时间才能完成。例如,您可能正在调用 API。
function App() {
console.log('Rendering App...');
const [x, setx] = React.useState(1);
const [x1, setx1] = React.useState(1);
const [x2, setx2] = React.useState(1);
const [x3, setx3] = React.useState(1);
const [x4, setx4] = React.useState(1);
const [x5, setx5] = React.useState(1);
const bclick = () => {
console.clear();
console.log("From bclick (batched: single render)");
setx1(x1 + 1);
setx2(x2 + 1);
setx3(x3 + 1);
setx4(x4 + 1);
setx5(x5 + 1);
setx(x + 1);
console.log("Calling bclick2");
bclick2();
};
const bclick2 = () => {
console.log("From bclick2");
};
const notBatched = () => {
console.clear();
console.log('From notBatched (multiple renders)');
setTimeout(() => setx1((prevState) => prevState+1),0);
setTimeout(() => setx1((prevState) => prevState+1),0);
setTimeout(() => setx1((prevState) => prevState+1),0);
};
return (
<div className="App">
<button onClick={bclick}>Click (will batch)</button>
<button onClick={notBatched}>Click (will not batch)</button>
</div>
);
}
ReactDOM.render(<App/>,document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
例如,如果您从 useEffect
调用 API:
useEffect(() => {
setIsLoading(true); // 1
const data = await fetchAPI();
setData(data); // 2
setIsLoading(false); // 3
},[]);
在这种情况下,React 将 运行 #1,然后,当 API 调用完成时,它将 运行 #2 和 #3 分开(未批处理)。不确定它为什么选择单独执行,因为将它们 运行 放在一起是安全的,但我确信 React 有它自己的原因。可能由于 API 调用而超时的整个块以某种方式标记为 shouldNotBatch
。我实际上不知道他们为此使用的内部逻辑是什么。
const fetchAPI = () => {
return new Promise((resolve) => {
setTimeout(() => resolve('DATA'),1500);
});
}
const App = () => {
console.log('Rendering App...');
const [isLoading,setIsLoading] = React.useState(false);
const [data,setData] = React.useState(null);
// I changed the `await` to `then` because SO snippets don't allow `await` in this case
React.useEffect(() => {
console.log('Running useEffect...');
setIsLoading(true); // 1
fetchAPI().then((data) => {
setData(data); // 2
setIsLoading(false); // 3
});;
},[]);
return(
<div>
<div>isLoading:{JSON.stringify(isLoading)}</div>
<div>data:{JSON.stringify(data)}</div>
</div>
);
};
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
React v18(2022 年 4 月)
显然 React v18 批处理也来自异步处理程序调用。
我正在学习 React,但很难理解正在发生的代码流。
这是我的 code:It 是一个功能性的 React 组件
function App() {
const [x, setx] = useState(1);
const [x1, setx1] = useState(1);
const [x2, setx2] = useState(1);
const [x3, setx3] = useState(1);
const [x4, setx4] = useState(1);
const [x5, setx5] = useState(1);
console.log("out3");
const bclick = () => {
setx1(x1 + 1);
setx2(x2 + 1);
setx3(x3 + 1);
setx4(x4 + 1);
setx5(x5 + 1);
setx(x + 1);
console.log("out1");
bclick2();
};
const bclick2 = () => {
console.log("out2");
};
console.log("out4");
return (
<div className="App">
{console.log("in")}
<button onClick={bclick} />
</div>
);
}
点击按钮后 console.log() 的输出: 输出1 输出2 输出3 输出4 在
Q> 单击按钮后,将执行多个不同的 setState。他们会重新评估组件或功能链(bclick 和 bclick2)完成执行,然后重新评估 App 组件。 根据我的输出,我意识到首先执行函数链。 那么 setState 是这样工作的吗? 代码流是否会先完成(不管函数的数量)然后重新评估功能组件?
这与 React 批处理 setState
调用有关,以优化渲染数量。通常你不必担心。
setState
调用是 async
。 React 将决定何时以及如何应用多个 setState
调用。
处理程序将始终在 React re-renders 之前完成 运行ning。这就是为什么您会在任何 re-renders.
之前看到bclick2()
调用 运行ning
我觉得 React 总是会在单个 re-render 中批处理多个 setState
调用。但是你可以看到,如果你在 setTimeout
中包装多个 setState
调用,React 将 re-render 多次,因为它无法知道这些超时需要多长时间才能完成。例如,您可能正在调用 API。
function App() {
console.log('Rendering App...');
const [x, setx] = React.useState(1);
const [x1, setx1] = React.useState(1);
const [x2, setx2] = React.useState(1);
const [x3, setx3] = React.useState(1);
const [x4, setx4] = React.useState(1);
const [x5, setx5] = React.useState(1);
const bclick = () => {
console.clear();
console.log("From bclick (batched: single render)");
setx1(x1 + 1);
setx2(x2 + 1);
setx3(x3 + 1);
setx4(x4 + 1);
setx5(x5 + 1);
setx(x + 1);
console.log("Calling bclick2");
bclick2();
};
const bclick2 = () => {
console.log("From bclick2");
};
const notBatched = () => {
console.clear();
console.log('From notBatched (multiple renders)');
setTimeout(() => setx1((prevState) => prevState+1),0);
setTimeout(() => setx1((prevState) => prevState+1),0);
setTimeout(() => setx1((prevState) => prevState+1),0);
};
return (
<div className="App">
<button onClick={bclick}>Click (will batch)</button>
<button onClick={notBatched}>Click (will not batch)</button>
</div>
);
}
ReactDOM.render(<App/>,document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
例如,如果您从 useEffect
调用 API:
useEffect(() => {
setIsLoading(true); // 1
const data = await fetchAPI();
setData(data); // 2
setIsLoading(false); // 3
},[]);
在这种情况下,React 将 运行 #1,然后,当 API 调用完成时,它将 运行 #2 和 #3 分开(未批处理)。不确定它为什么选择单独执行,因为将它们 运行 放在一起是安全的,但我确信 React 有它自己的原因。可能由于 API 调用而超时的整个块以某种方式标记为 shouldNotBatch
。我实际上不知道他们为此使用的内部逻辑是什么。
const fetchAPI = () => {
return new Promise((resolve) => {
setTimeout(() => resolve('DATA'),1500);
});
}
const App = () => {
console.log('Rendering App...');
const [isLoading,setIsLoading] = React.useState(false);
const [data,setData] = React.useState(null);
// I changed the `await` to `then` because SO snippets don't allow `await` in this case
React.useEffect(() => {
console.log('Running useEffect...');
setIsLoading(true); // 1
fetchAPI().then((data) => {
setData(data); // 2
setIsLoading(false); // 3
});;
},[]);
return(
<div>
<div>isLoading:{JSON.stringify(isLoading)}</div>
<div>data:{JSON.stringify(data)}</div>
</div>
);
};
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
React v18(2022 年 4 月)
显然 React v18 批处理也来自异步处理程序调用。