React中useEffect结合async时,isLoading状态为何未被批量更新?
React中async函数里的状态更新为何能立即显示Loading?
核心原因是await会打断同步执行流程,让React无法批量处理第一次的状态更新,具体执行逻辑如下:
- 组件挂载后,
useEffect调用fetchRepos函数,首先执行setIsLoading(true)。 - 接下来遇到
await fetch(...)——此时JavaScript会暂停当前async函数的执行,把控制权交还给浏览器事件循环。 - React检测到当前同步代码已经暂停,没有后续的同步状态更新待处理,就会立即执行
setIsLoading(true)对应的状态更新,触发组件重新渲染,因此JSX能获取到isLoading=true,显示“Loading...”文本。 - 等GitHub API请求完成后,await恢复async函数的执行,接着执行
setIsLoading(false),这时候又会触发一次渲染,切换显示“Done!”。
总结:React的批量更新仅在同步执行的代码块内生效,一旦遇到异步中断(比如await、setTimeout、Promise.then),之前的状态更新会被立即处理,不会等到函数完全结束。
举个反例,如果去掉await改成同步代码:
async function fetchRepos() { setIsLoading(true); setIsLoading(false); // 同步执行两次状态更新 }
此时React会批量处理这两次更新,只会渲染最后一次的false,你就看不到Loading状态了。
你的完整代码:
<div id="root"></div> <script crossorigin src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script> <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <script type="text/babel"> const { createRoot } = ReactDOM; const { StrictMode, useEffect, useState } = React; function App() { const [isLoading, setIsLoading] = useState(false); const [repos, setRepos] = useState([]); useEffect(() => { async function fetchRepos() { setIsLoading(true); const reposFetch = await fetch("https://api.github.com/users/OthmaneNissoukin/repos"); const data = await reposFetch.json(); setIsLoading(false); } fetchRepos(); }, []); return ( <div className="App"> <h1>Hello CodeSandbox</h1> {isLoading ? <h2>Loading...</h2> : <div>Done!</div>} </div> ); } const root = createRoot(document.getElementById("root")); root.render(<StrictMode><App /></StrictMode>); </script>
内容的提问来源于stack exchange,提问作者Othmane NISS
相关产品推荐
相关产品推荐

