使用React Context API通过接口请求设置上下文数据失败排查
React Context API 数据无法更新问题排查与解决
问题现象
通过API请求获取数据后,根App组件、AppProvider及AppContext初始化函数中均能打印出接口返回的数据,但上下文appState内的appData始终为空数组,Test组件无法获取到接口数据。
代码问题分析
1. useReducer 初始化仅执行一次
App组件中data初始值为空数组,AppProvider首次渲染时,getAppContextInit使用空数组初始化useReducer。后续App组件通过setData更新data,AppProvider会重新渲染,但useReducer的初始化函数只会在组件首次挂载时执行一次,不会因传入的初始化参数变化而重新计算状态,导致上下文状态无法同步更新后的data。
2. appReducer 实现不符合规范
当前的appReducer存在两个核心问题:
- 未接收
action参数,也未根据action类型更新状态,无法通过dispatch触发状态变更 - 尝试从
appState中获取dataSource属性,但初始化的appState中并没有该字段,会导致prop('dataSource')(appState)报错,进而影响localStorage存储逻辑
3. 缺少主动更新上下文状态的机制
整个流程仅依赖useReducer的初始化来设置状态,没有在data更新时主动触发上下文状态的更新逻辑,这是导致数据无法同步到上下文的关键原因。
修复方案
步骤1:完善appReducer,支持action更新状态
修改appReducer,添加action处理逻辑,支持更新appData的action:
import { prop } from 'ramda'; // 假设使用ramda的prop方法 export const appReducer = (appState, action) => { let updatedState = { ...appState }; switch (action.type) { case 'UPDATE_APP_DATA': updatedState = { ...appState, appData: action.payload }; break; default: return appState; } // 存储更新后的状态到localStorage const store = `${action.dataSource}-appState`; window.localStorage.setItem(store, JSON.stringify(updatedState)); return updatedState; };
步骤2:修改AppProvider,监听data变化并dispatch更新
在AppProvider中添加useEffect,监听data的变化,当data更新时,通过appAction(dispatch)触发状态更新:
import { useReducer, useEffect } from 'react'; import { getAppContextInit } from './AppContext'; import { appReducer } from './appReducer'; const AppProvider = ({ source, data, children }) => { const [appState, appAction] = useReducer( appReducer, getAppContextInit(source, data), ); // 当data变化时,更新上下文状态 useEffect(() => { if (data.length > 0) { // 避免初始空数组触发更新 appAction({ type: 'UPDATE_APP_DATA', payload: data, dataSource: source, }); } }, [data, source, appAction]); return ( <AppContext.Provider value={{ appState, appAction }}> {children} </AppContext.Provider> ); }; export default AppProvider;
步骤3:修正getAppContextInit的localStorage存储逻辑
添加JSON.parse的异常处理,避免解析null报错:
import { createContext, useContext } from 'react'; export const getAppContextInit = (dataSource, data) => { let storedState = null; try { storedState = JSON.parse( window.localStorage.getItem(`${dataSource}-appState`), ); } catch (e) { console.error('解析localStorage数据失败:', e); } // 返回本地存储的appState,若不存在或解析失败则返回新对象 return storedState ? storedState : { appData: data, }; }; // 定义上下文类型(TypeScript环境) export type appContextType = { appState: { appData: any[]; }; appAction: React.Dispatch<any>; }; export const AppContext = createContext<appContextType>({ appState: { appData: [], }, appAction: () => {}, }); export const useAppContext = () => useContext(AppContext);
步骤4:验证Test组件数据获取
此时Test组件中通过useAppContext获取的appState.appData会同步接口返回的数据:
const Test = () => { const { appState } = useAppContext(); console.log('appState', appState); // 现在会输出预期的接口数据 return ( <div> Test App Component </div> ) } export default Test;
核心修复逻辑总结
- 通过
useEffect监听data变化,主动触发dispatch更新上下文状态,弥补useReducer初始化仅执行一次的限制 - 完善
appReducer的action处理逻辑,使其符合React reducer的规范 - 增加localStorage解析的异常处理,避免潜在报错
内容的提问来源于stack exchange,提问作者Raj
相关产品推荐
相关产品推荐

