You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中setState在同步与异步回调中重渲染时机差异的疑问

React setState 同步/异步场景下的重渲染差异问题

疑问点:第一个示例中,useEffect同步代码里调用setAllMemes时,组件会等待该useEffect内所有代码执行完毕后才触发重渲染;第二个示例中,fetch的then异步回调里调用setAllMemes时,组件会在回调代码未执行完就触发重渲染,为何会出现这种差异?


第一个代码示例

import React from "react"

export default function Meme() {
    console.log("react render")
    const [allMemes, setAllMemes] = React.useState([])

    console.log("allMemes outside the use effect" , allMemes)    

    React.useEffect(() => {
    
        console.log("before setAllMemes")
        setAllMemes(["string1","string2"]);
        console.log("after setAllMemes");
            
        console.log("allMemes inside the use effect" , allMemes);
    }, [])

    return (
        <main>
        
        </main>
    )
}

控制台输出

›react render
›allMemes outside the use effect,[]
›before setAllMemes
›after setAllMemes
›allMemes inside the use effect,[]
›react render
›allMemes outside the use effect,["string1", "string2"]

第二个代码示例

import React from "react"

export default function Meme() {
     console.log("react render")
     const [allMemes, setAllMemes] = React.useState([])

     console.log("allMemes outside the use effect" , allMemes)    

     React.useEffect(() => {
        fetch("https://api.imgflip.com/get_memes")
            .then(res => res.json())
            .then(data => {
                console.log("before setAllMemes")
                setAllMemes(data.data.memes);
                console.log("after setAllMemes");
                })
        console.log("allMemes inside the use effect" , allMemes);
        }, [])

        return (
            <main>
        
            </main>
        )
    }

控制台输出

›react render
›allMemes outside the use effect,[]
›allMemes inside the use effect,[]
›before setAllMemes
›react render
›allMemes outside the use effect [array of object from the api]
›after setAllMemes

原因解释

核心差异来自JavaScript事件循环机制和React状态更新的批量处理策略:

  1. 同步代码场景(第一个示例)
    useEffect的回调是同步执行的,在这个同步任务流中,React会开启批量更新模式:调用setAllMemes时,React不会立刻触发重渲染,而是将状态更新任务加入队列,等当前同步代码(整个useEffect回调)执行完毕后,再一次性处理队列中的更新并触发重渲染。这就是为什么after setAllMemes和allMemes inside the use effect会先打印,之后才出现第二次react render。

  2. 异步回调场景(第二个示例)
    fetch的then回调属于微任务,会在当前同步任务(useEffect内的console.log("allMemes inside the use effect" , allMemes);)执行完成后,才进入事件循环队列等待执行。而React在Promise、setTimeout这类异步回调中,默认不会启用批量更新(React 18之前的行为,若在React 18的并发模式下会有自动批量更新,但本次输出符合旧行为特征)。当在then里调用setAllMemes时,React会立即触发重渲染流程,此时then回调里的console.log("after setAllMemes");还未执行——因为重渲染任务会插入事件循环,优先于剩余的回调代码执行,所以会先看到react render,再看到after setAllMemes。

另外补充:useEffect里打印的allMemes始终是初始值,是因为useEffect回调捕获了组件首次渲染时的状态闭包,即便调用了setState,当前回调内的状态变量不会立刻更新。


内容的提问来源于stack exchange,提问作者mohamed azaiez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 17:13:25