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

React从API调用设置状态遇异步问题:无法渲染账户列表

问题描述

我搞不清问题出在哪,感觉碰到了竞态条件但找不到原因。我尝试在useEffect里从API加载一组"accounts"数据,请求完成后调用setAccounts()设置状态。网络面板显示API响应正常,但渲染时用状态变量accounts却显示不出内容。

相关代码片段(idToken来自Auth0):

const [accounts, setAccounts] = useState();
const fetchAccounts = async () => {
    const url = "http://0.0.0.0:8000/api/v1/account/";
    const response = await fetch(url, {
        method: "get",
        headers: new Headers({
            "Content-Type": "application/json",
            Authorization: "Bearer " + idToken,
        }),
    }).then((response) => response.json());
    console.log("debug", response);
    setAccounts(response);
};

useEffect(() => {
    const getToken = async () => {
        if (idToken == null) {
            const id_token = await getIdTokenClaims();
            setIdToken(id_token.__raw);
        }
    };
    getToken();
    if (idToken) {
        console.log("ID token check", idToken);
        fetchAccounts();
        getClientPortfolios();
    }
    console.log("accounts", accounts); // 这里打印不出内容
}, [idToken]);

return (
    <div>
        {accounts.map((account) => {
            <div key={account.id}>{account.name}</div>;
        })}
    </div>
)

目前的问题:状态变量似乎没被设置,而且map函数抛出错误:Uncaught TypeError: Cannot read properties of undefined (reading 'map')

我的实现是不是有问题?

注:fetchAccounts中的debug语句输出正常,能看到返回的accounts数据


问题分析与修复

1. 初始状态未设置为空数组

useState()默认初始值是undefined,组件首次渲染时accounts是undefined,调用map必然报错。把初始状态设为空数组即可避免这个问题:

const [accounts, setAccounts] = useState([]);

2. useEffect异步逻辑执行时机错误

getToken是异步函数,但你直接调用后没有等待它完成就判断idToken。第一次执行useEffect时idToken还是null,不会进入请求分支;等getToken完成调用setIdToken后,useEffect才会再次触发,此时idToken有值才会调用接口。优化逻辑确保token获取完成后再请求数据:

useEffect(() => {
    const loadData = async () => {
        let currentIdToken = idToken;
        if (!currentIdToken) {
            const id_token = await getIdTokenClaims();
            currentIdToken = id_token.__raw;
            setIdToken(currentIdToken);
        }
        if (currentIdToken) {
            console.log("ID token check", currentIdToken);
            await fetchAccounts();
            getClientPortfolios();
        }
    };
    loadData();
    // 注:这里打印accounts永远是旧值,因为setState是异步的,状态更新不会同步反映在当前闭包中
}, [idToken]);

3. map回调未返回JSX元素

你的map回调用了大括号但没有显式返回元素,导致渲染时无内容。两种修正方式:

// 方式1:用小括号省略return
{accounts.map((account) => (
    <div key={account.id}>{account.name}</div>
))}

// 方式2:显式return
{accounts.map((account) => {
    return <div key={account.id}>{account.name}</div>;
})}

4. setState异步性导致console.log无更新

setAccounts是异步操作,调用后不会立刻更新accounts状态,所以useEffect里的console.log("accounts", accounts)打印的是当前闭包中的旧值,这是正常现象,无需纠结。


修正后的完整代码片段
const [accounts, setAccounts] = useState([]);
const fetchAccounts = async () => {
    const url = "http://0.0.0.0:8000/api/v1/account/";
    try {
        const response = await fetch(url, {
            method: "get",
            headers: new Headers({
                "Content-Type": "application/json",
                Authorization: "Bearer " + idToken,
            }),
        });
        const data = await response.json();
        console.log("debug", data);
        setAccounts(data);
    } catch (err) {
        console.error("Failed to fetch accounts:", err);
    }
};

useEffect(() => {
    const loadData = async () => {
        let currentIdToken = idToken;
        if (!currentIdToken) {
            const id_token = await getIdTokenClaims();
            currentIdToken = id_token.__raw;
            setIdToken(currentIdToken);
        }
        if (currentIdToken) {
            console.log("ID token check", currentIdToken);
            await fetchAccounts();
            getClientPortfolios();
        }
    };
    loadData();
}, [idToken]);

return (
    <div>
        {accounts.map((account) => (
            <div key={account.id}>{account.name}</div>
        ))}
    </div>
);

内容的提问来源于stack exchange,提问作者Lauren Boland

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 08:57:11