React中同状态关联快慢API请求的渲染优化方案咨询
优化方案
你原有实现存在三个可优化的核心问题:
- 首次实现中串行
await两个无依赖的请求,额外拉长了用户等待时间 - 直接给
useEffect传入async函数会触发React警告,因为async函数返回的Promise会被识别为清理函数,存在内存泄漏风险 - 拆分为两个
useEffect的写法逻辑分散,维护成本更高
推荐实现方案
不需要拆分多个useEffect,在同一个副作用内并行发起两个请求,分别处理返回结果即可:
const [pageState, setPageState] = useState({}) // 控制整页加载状态,快请求返回后设为false const [loadingWhole, setLoadingWhole] = useState(true) // 控制局部内容加载状态,慢请求返回后设为false const [loadingPart, setLoadingPart] = useState(true) useEffect(() => { // 处理快请求 const fetchQuick = async () => { try { const quickRes = await quick_api_call // 函数式更新避免状态覆盖 setPageState(prev => ({...prev, ...quickRes.data})) setLoadingWhole(false) } catch (e) { setLoadingWhole(false) // 单独处理快请求错误逻辑 } } // 处理慢请求 const fetchSlow = async () => { try { const slowRes = await slow_api_call setPageState(prev => ({...prev, ...slowRes.data})) setLoadingPart(false) } catch (e) { setLoadingPart(false) // 单独处理慢请求错误逻辑 } } // 并行发起两个请求,不需要等待另一个返回 fetchQuick() fetchSlow() }, []) return ( <React.Fragment> {loadingWhole ? <SpinnerComponent /> : ( <div> {/* 不依赖慢请求的主体内容直接渲染 */} <MainContent data={pageState} /> {/* 依赖慢请求的局部内容单独加加载状态 */} {loadingPart ? <PartSpinner /> : <PartialContent data={pageState.slowPart} />} </div> )} </React.Fragment> )
方案优势
- 两个请求并行发起,快请求返回后立即渲染主体内容,用户感知加载速度大幅提升
- 所有请求逻辑内聚在同一个
useEffect中,后续维护更方便 - 采用函数式更新state,避免两个请求先后返回时出现状态覆盖问题
- React 18及以上版本会自动批处理异步回调中的多次
setState操作,若两个请求接近同时返回,只会触发一次重渲染,无额外性能开销 - 每个请求独立处理错误,不会因为单个请求失败导致整个页面加载失败,容错性更强
关于两次渲染的疑问
你担心的两次渲染实际上完全不需要优化:
- 两次渲染都是业务需要的有效渲染:第一次向用户展示可交互的主体内容,第二次补全依赖慢请求的局部内容,属于体验提升而非性能损耗
- 现代React的重渲染性能极高,这种场景下的两次渲染对性能的影响可以忽略不计,远低于让用户多等待几秒的体验损失
- 如果强制合并为一次渲染,反而会回到最初需要等待所有请求返回才能渲染页面的问题,得不偿失
内容的提问来源于stack exchange,提问作者Xiaoye Yang
相关产品推荐
相关产品推荐

