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

React中useState异步传递问题:首次点击URL未生效

React状态异步更新导致首次点击fetch请求失效问题

问题场景

点击分类复选框时,需先请求子分类数据并拼接出商品查询URL,再用该URL发起商品请求,但首次点击时,后续fetch无法获取刚更新的categoryUrl状态,导致请求异常;使用useEffect监听状态仍存在延迟问题。

现有代码

// 初始化加载一级分类
useEffect(() => {
    fetch('http://api.tmweb.ru/category?filter[depth]=1&filter[type]=0',{
        headers: {
            'Accept': 'application/json',
            'Authorization': token,
        }
    })
        .then(res => res.json())
        .then((result) => setCategory(result.data))
}, [])

useEffect(()=>{
    console.log(categoryUrl)
}, [categoryUrl])

// 点击分类复选框触发的函数
let getDataCategory = async(event) => {
    let id = event.currentTarget.id;

    if (event.target.checked){
        await fetch(`http://api.tmweb.ru/category?filter[parent_id][in]=${id}&filter[type]=0`,{
            headers: {
                'Accept': 'application/json',
                'Authorization': token,
            },
        })
            .then(res => res.json())
            .then((result) => {
                // 更新子分类状态
                setCategorySub([...categorySub,...result.data]);
                // 拼接请求商品的URL
                let url2 = `http://api.tmweb.ru/object?filter[category_id][in][]=${id}&`
                result.data.forEach(item => {url2 += 'filter[category_id][in][]=' + item.id + '&'})
                // 更新URL状态
                setCategoryUrl(url2)
            });

        // 首次点击时,此处获取的是旧的categoryUrl
        await fetch(`${categoryUrl}&filter[type]=0`,{
            headers: {
                'Accept': 'application/json',
                'Authorization': token,
            },
        })
            .then(res => res.json())
            .then((result) => {
                console.log(result)
                setCategoryProd([...categoryProd,...result.data])
            })
    } else {
        // 取消勾选时移除对应数据
        setCategorySub((prevData) => {
            return prevData.filter((item) => item.parent_id !== +id);
        });
        setCategoryProd((prevData) =>{
            return prevData.filter((item) => item.category_id !== +id);
        })
    }
}

核心原因

React的setState是异步操作,调用setCategoryUrl(url2)后,categoryUrl不会立即更新为最新值。因此紧接着的fetch请求仍使用更新前的旧状态,导致首次点击失效。

解决方案

方案1:直接使用生成的url2发起请求(推荐)

无需依赖状态存储拼接好的URL,直接在生成url2后发起请求,绕过异步状态更新的问题:

let getDataCategory = async(event) => {
    let id = event.currentTarget.id;

    if (event.target.checked){
        await fetch(`http://api.tmweb.ru/category?filter[parent_id][in]=${id}&filter[type]=0`,{
            headers: {
                'Accept': 'application/json',
                'Authorization': token,
            },
        })
            .then(res => res.json())
            .then((result) => {
                setCategorySub([...categorySub,...result.data]);
                let url2 = `http://api.tmweb.ru/object?filter[category_id][in][]=${id}&`
                result.data.forEach(item => {url2 += 'filter[category_id][in][]=' + item.id + '&'})
                setCategoryUrl(url2);
                // 直接使用本地变量url2发起请求
                return fetch(`${url2}&filter[type]=0`,{
                    headers: {
                        'Accept': 'application/json',
                        'Authorization': token,
                    },
                })
            })
            .then(res => res.json())
            .then((result) => {
                console.log(result)
                setCategoryProd([...categoryProd,...result.data])
            })
    } else {
        setCategorySub((prevData) => {
            return prevData.filter((item) => item.parent_id !== +id);
        });
        setCategoryProd((prevData) =>{
            return prevData.filter((item) => item.category_id !== +id);
        })
    }
}

方案2:用useEffect监听categoryUrl触发请求

若必须依赖categoryUrl状态,将商品请求逻辑放到useEffect中,确保状态更新后再执行:

// 初始化加载一级分类
useEffect(() => {
    fetch('http://api.tmweb.ru/category?filter[depth]=1&filter[type]=0',{
        headers: {
            'Accept': 'application/json',
            'Authorization': token,
        }
    })
        .then(res => res.json())
        .then((result) => setCategory(result.data))
}, [])

// 监听categoryUrl变化,触发商品请求
useEffect(()=>{
    // 避免初始空值触发无效请求
    if(categoryUrl){
        fetch(`${categoryUrl}&filter[type]=0`,{
            headers: {
                'Accept': 'application/json',
                'Authorization': token,
            },
        })
            .then(res => res.json())
            .then((result) => {
                console.log(result)
                setCategoryProd([...categoryProd,...result.data])
            })
    }
}, [categoryUrl, categoryProd, token])

// 点击函数仅负责更新子分类和categoryUrl
let getDataCategory = async(event) => {
    let id = event.currentTarget.id;

    if (event.target.checked){
        await fetch(`http://api.tmweb.ru/category?filter[parent_id][in]=${id}&filter[type]=0`,{
            headers: {
                'Accept': 'application/json',
                'Authorization': token,
            },
        })
            .then(res => res.json())
            .then((result) => {
                setCategorySub([...categorySub,...result.data]);
                let url2 = `http://api.tmweb.ru/object?filter[category_id][in][]=${id}&`
                result.data.forEach(item => {url2 += 'filter[category_id][in][]=' + item.id + '&'})
                setCategoryUrl(url2);
            });
    } else {
        setCategorySub((prevData) => {
            return prevData.filter((item) => item.parent_id !== +id);
        });
        setCategoryProd((prevData) =>{
            return prevData.filter((item) => item.category_id !== +id);
        })
    }
}

补充说明

  • 方案1更高效,避免了异步状态更新的等待开销,直接使用本地变量完成请求。
  • 方案2需注意useEffect的依赖数组,确保所有用到的状态、变量都加入,避免闭包导致的旧值问题。

内容的提问来源于stack exchange,提问作者Александр

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 15:55:34