React异步函数内调用setState的执行机制疑问
问题:React异步函数中setState的更新时机
我正在学习如何从React调用REST API,对在异步函数中调用React setState的行为感到困惑。以下是我的组件代码:
import { useState, useEffect } from "react"; import Places from "./Places.jsx"; import Error from "./Error.jsx"; import { sortPlacesByDistance } from "../loc.js"; import { fetchAvailablePlaces } from "../http.js"; export default function AvailablePlaces({ onSelectPlace }) { const [isFetching, setIsFetching] = useState(false); const [availablePlaces, setAvailablePlaces] = useState([]); const [error, setError] = useState(); useEffect(() => { async function fetchPlaces() { setIsFetching(true); try { const availablePlaces = await fetchAvailablePlaces(); setAvailablePlaces(sortedPlaces); setIsFetching(false); } catch (error) { setError({ message: error.message || "Could not fetch places, please try again later.", }); setIsFething(false); } } fetchPlaces(); }, []); if (error) { return <Error title="An error occurred!" message={error.message} />; } return ( <Places title="Available Places" places={availablePlaces} isLoading={isFetching} loadingText="Fetching place data..." fallbackText="No places available." onSelectPlace={onSelectPlace} /> ); }
我的问题是:React会等到整个异步函数执行完成后才更新状态吗?还是会立即更新状态并重新渲染组件?如果是后者,原因是什么?
我感到困惑的点在于,在普通同步函数(如下例)中:
function a() { setIsFetching(true) // .... a lot more logic is done below }
React似乎会等到函数执行完成后才更新状态并重新渲染组件。为什么异步函数(即使包含await关键字)中状态会在函数执行完成前就更新?
解答
结论先行:异步函数里的setState不会等整个函数执行完,会在调用后尽快触发状态更新和重渲染,核心原因和JavaScript的事件循环机制直接相关:
同步函数里的状态更新逻辑
同步函数的代码是连续执行的,React为了避免频繁重渲染影响性能,会把同步函数中所有的setState调用合并成一次更新。比如你在同步函数里连续调用多次setState,React会等当前同步任务完全执行完毕后,才一次性处理所有状态变更,触发一次重渲染。这就是你觉得“同步函数要执行完才更新状态”的原因。异步函数里的状态更新逻辑
当异步函数执行到await关键字时,JavaScript会暂停当前异步函数的执行,把它挂起,转而处理事件循环里的其他任务。这就给了React处理状态更新的机会:- 比如你的
fetchPlaces函数中,先调用setIsFetching(true),此时React标记状态需要更新,但还在同步执行阶段(没到await),暂时不触发渲染; - 执行到
await fetchAvailablePlaces()时,JavaScript把API请求交给浏览器处理,暂停fetchPlaces的执行,回到事件循环; - 此时事件循环没有其他同步任务,React就会处理之前的状态更新,把
isFetching改成true,触发组件重渲染,展示加载状态; - 等浏览器拿到API响应,
fetchAvailablePlaces的Promise兑现,JavaScript才会继续执行fetchPlaces剩下的代码,调用setAvailablePlaces和setIsFetching(false),再次触发状态更新和重渲染。
- 比如你的
简单来说,await把异步函数切成了两段执行,中间的空隙让React有机会处理状态更新,所以不会等整个异步函数跑完才更新。
另外提两个你代码里的小问题:
- try块里的
sortedPlaces未定义,应该是sortPlacesByDistance(availablePlaces); - catch块里的
setIsFething拼写错误,正确是setIsFetching。
内容的提问来源于stack exchange,提问作者codean
相关产品推荐
相关产品推荐

