React+TypeScript中Context内useState设置后仍为空数组的问题
问题:React Context中useState的setState不生效,state始终为空数组
尝试在React Context结合TypeScript接口管理useState变量,目标是从someArray中获取数据并存入state,但stateData始终输出[],无法更新。
原代码片段
1. 上下文组件代码
import { createContext, useState } from 'react'; export interface exportedInterface { stateData: NftsData[]; fetchUnstakedNFTs: () => void; } interface Data { dataString: string, dataNumber: number, } export const customContext = createContext<exportedInterface>(); const CustomContext: FC = ({ children = null as any }) => { const [stateData, setStateData] = useState<Data[]>([]); let listDataArray: Data[] = []; export const fetchSomeArray = async () => { for (let item of someArray) { await fetch(item.url) .then(resp => resp.json() ).then((json) => { const listData: Data = { { dataString: json.stringData, dataNumber: json.numberData } listDataArray.push(listData) } } setStateData(listDataArray) // 此处未生效 } console.log('stateData: ', stateData) return ( <CustomContext.Provider value={{ stateData, fetchUnstakedNFTs, }} > {children} </CustomContext.Provider> ); }
2. 自定义Hook代码
import { useContext } from 'react'; import { CustomContext } from 'aboveFile'; export const useCustomContext = () => { const context = useContext(CustomContext); if (context === undefined) { throw new Error( 'CustomContext undefined' ); } return context; };
3. 使用组件代码
import { useCustomContext } from 'aboveFile'; export default function App() { const { fetchSomeArray } = useCustomContext(); useEffect(() => { fetchSomeArray() }, []) return ( ... ) }
问题现象
执行后控制台始终输出:stateData: [],无论等待多久都无法更新。
错误分析与修复方案
关键问题列表
- 函数命名不匹配:Context中定义的异步函数是
fetchSomeArray,但Provider的value中传入的是fetchUnstakedNFTs,且接口exportedInterface中声明的也是fetchUnstakedNFTs,导致组件通过useCustomContext获取的fetchSomeArray根本不存在,函数从未被执行。 - 语法错误:创建
listData对象时多嵌套了一层大括号,导致代码报错,无法执行到setStateData步骤。 - 变量作用域问题:
listDataArray定义在组件函数内部,每次组件渲染都会被重置为[],存在闭包风险;且异步循环中直接修改数组后setState,不如用函数内临时变量存储结果更安全。 - Context初始化缺失默认值:
createContext未传入符合exportedInterface的默认值,不符合TypeScript规范。 - 组件与Context对象命名冲突:组件名为
CustomContext,导出的Context对象名为customContext,容易混淆,建议组件命名为CustomContextProvider。
修复后的完整代码
1. 修复后的上下文组件
import { createContext, useState, FC, ReactNode } from 'react'; // 统一数据类型定义 interface Data { dataString: string; dataNumber: number; } // 修正Context接口,与内部state类型对齐 export interface CustomContextType { stateData: Data[]; fetchSomeArray: () => Promise<void>; } // 传入符合类型的默认值 export const CustomContext = createContext<CustomContextType>({ stateData: [], fetchSomeArray: async () => {} }); // 组件重命名,避免与Context对象冲突 export const CustomContextProvider: FC<{ children: ReactNode }> = ({ children }) => { const [stateData, setStateData] = useState<Data[]>([]); // 函数命名与接口保持一致 const fetchSomeArray = async () => { // 临时数组在函数内部声明,避免组件渲染重置 const tempArray: Data[] = []; for (const item of someArray) { try { // 用await替代.then,避免回调嵌套和错误捕获漏洞 const resp = await fetch(item.url); const json = await resp.json(); // 修正对象语法错误 const listData: Data = { dataString: json.stringData, dataNumber: json.numberData }; tempArray.push(listData); } catch (err) { console.error('Fetch error:', err); } } // 更新state setStateData(tempArray); }; // 推荐用useEffect监听state变化,而非组件顶层console.log // useEffect(() => { // console.log('stateData updated:', stateData); // }, [stateData]); return ( <CustomContext.Provider value={{ stateData, fetchSomeArray }} > {children} </CustomContext.Provider> ); };
2. 修复后的自定义Hook
import { useContext } from 'react'; import { CustomContext } from './path-to-context'; export const useCustomContext = () => { const context = useContext(CustomContext); if (!context) { throw new Error('useCustomContext must be used within a CustomContextProvider'); } return context; };
3. 修复后的使用组件
import { useEffect } from 'react'; import { useCustomContext } from './path-to-hook'; export default function App() { const { fetchSomeArray, stateData } = useCustomContext(); useEffect(() => { fetchSomeArray(); }, [fetchSomeArray]); // 添加依赖,符合React Hooks规则 console.log('App stateData:', stateData); return ( <div> {/* 渲染stateData内容 */} {stateData.map((item, index) => ( <div key={index}> <p>{item.dataString}</p> <p>{item.dataNumber}</p> </div> ))} </div> ); }
额外说明
- 组件顶层的
console.log会在初始渲染时输出空数组,state更新后会再次渲染并输出新值,若只想看更新后的结果,建议用useEffect监听stateData变化。 - 异步函数中添加
try/catch可捕获请求错误,避免整个循环中断。 - 确保
someArray是已定义的变量,且每个item包含有效的url字段。
内容的提问来源于stack exchange,提问作者cris.sol
相关产品推荐
相关产品推荐

