React Context中无法将API响应数据设置到State值求助
问题:React Context中无法将API响应数据设置到状态
在构建基础React Context调用无认证API时,确认能获取正确响应且已做类型安全处理,但fetchedData状态始终保持默认空数组。
相关代码
API响应示例
[ { "id": "C8144412-E7BE-4CC4-BB0A-294938E38BD6", "appName": "some App", "p1AlertCount": 1, "p2AlertCount": 2, "appScore": 20, "appStatus": true, "message": "up and running" }, ... ]
Context API代码
import { createContext, useContext, useEffect, useState } from "react"; import AppStatus from "./types"; import axios from "axios"; const AppContext = createContext<AppStatus[] | null>(null); const api_endpoint: string = `http://localhost:5289/AppStatus`; const AppProvider = ({ children }: { children: React.ReactNode }) => { const [isLoading, setLoading] = useState<boolean>(true); const [fetchedData, setFetchedData] = useState<AppStatus[]>([]); const fetchData = async (url: string) => { try { const response = await axios.get(url); const appData: AppStatus[] = (await response.data) as AppStatus[]; if (response.status != 200 || response.statusText != "OK") { throw new Error("failed to fetch"); } setLoading(false); setFetchedData(appData); console.log(appData); console.log(fetchedData); } catch (error) { throw new Error(`error is ${error}`); } }; useEffect(() => { fetchData(api_endpoint); }, []); return ( <AppContext.Provider value={{ isLoading, fetchedData }}> {children} </AppContext.Provider> ); }; const useGlobalContext = () => { return useContext(AppContext); }; export { AppContext, AppProvider, useGlobalContext };
React响应类型定义
export default interface AppStatus { id: string; appName: string; p1AlertCount: number; p2AlertCount: number; appScore: number; appStatus: boolean; message: string; }
API模型代码
namespace App_Health_Mock_API.Model { public class AppDataModel { public required string Id { get; set; } public required string AppName { get; set; } public required int P1AlertCount { get; set; } = 0; public required int P2AlertCount { get; set; } = 0; public required int AppScore { get;set; } public required Boolean AppStatus { get; set; } = true; public required string Message { get; set; } } }
解决方案
1. 修复重复await问题
axios.get已经通过await拿到完整响应,response.data直接是返回的数据,不需要再次await。重复await会导致appData变成Promise对象而非实际数组,无法正确设置状态。
修改代码:
// 错误写法 const appData: AppStatus[] = (await response.data) as AppStatus[]; // 正确写法 const appData: AppStatus[] = response.data as AppStatus[];
2. 修正Context类型不匹配问题
原createContext的类型是AppStatus[] | null,但Provider传递的是包含isLoading和fetchedData的对象,类型完全不匹配,会导致useGlobalContext返回值可能为null,后续使用异常。
创建正确的Context类型并更新初始化:
// 新增Context类型定义 interface AppContextType { isLoading: boolean; fetchedData: AppStatus[]; } // 用正确类型初始化Context,设置合理默认值 const AppContext = createContext<AppContextType>({ isLoading: true, fetchedData: [] });
3. 理解React setState的异步特性
setFetchedData是异步操作,调用后立即console.log(fetchedData)会拿到更新前的旧值,不能以此判断状态是否设置成功。验证状态更新可通过:
- 在子组件中使用
useGlobalContext获取状态并渲染 - 添加
useEffect监听fetchedData变化:
useEffect(() => { console.log("fetchedData updated:", fetchedData); }, [fetchedData]);
4. 优化错误处理(可选)
直接throw new Error会导致组件崩溃,建议添加错误状态并处理:
const [error, setError] = useState<string | null>(null); // 在catch块中设置错误状态 catch (error) { setError(`Failed to fetch data: ${(error as Error).message}`); setLoading(false); } // 在Provider中传递error状态 <AppContext.Provider value={{ isLoading, fetchedData, error }}>
修改后的完整Context代码
import { createContext, useContext, useEffect, useState } from "react"; import AppStatus from "./types"; import axios from "axios"; interface AppContextType { isLoading: boolean; fetchedData: AppStatus[]; error: string | null; } const AppContext = createContext<AppContextType>({ isLoading: true, fetchedData: [], error: null }); const api_endpoint: string = `http://localhost:5289/AppStatus`; const AppProvider = ({ children }: { children: React.ReactNode }) => { const [isLoading, setLoading] = useState<boolean>(true); const [fetchedData, setFetchedData] = useState<AppStatus[]>([]); const [error, setError] = useState<string | null>(null); const fetchData = async (url: string) => { try { const response = await axios.get(url); if (response.status !== 200 || response.statusText !== "OK") { throw new Error("Failed to fetch data"); } const appData: AppStatus[] = response.data as AppStatus[]; setFetchedData(appData); setError(null); } catch (error) { setError(`Error: ${(error as Error).message}`); setFetchedData([]); } finally { setLoading(false); } }; useEffect(() => { fetchData(api_endpoint); }, []); // 验证状态更新 useEffect(() => { console.log("Updated fetchedData:", fetchedData); }, [fetchedData]); return ( <AppContext.Provider value={{ isLoading, fetchedData, error }}> {children} </AppContext.Provider> ); }; const useGlobalContext = () => { return useContext(AppContext); }; export { AppContext, AppProvider, useGlobalContext };
验证方法
在任意子组件中使用Context:
import { useGlobalContext } from "./AppContext"; const AppList = () => { const { isLoading, fetchedData, error } = useGlobalContext(); if (isLoading) return <div>Loading...</div>; if (error) return <div>{error}</div>; return ( <ul> {fetchedData.map(app => ( <li key={app.id}>{app.appName}</li> ))} </ul> ); }; export default AppList;
内容的提问来源于stack exchange,提问作者Ritwik Nandi
相关产品推荐
相关产品推荐

