React Context报错:无法解构undefined的setAppState属性
React App Context 未定义错误排查
我正在配置全局App Context时遇到了一个难以排查的错误,查阅相关问题后仍无法解决。报错显示AppContext返回undefined,具体错误信息如下:
TypeError: Cannot destructure property 'setAppState' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined.
相关代码文件
appContext.js
import React, { createContext } from 'react'; const AppContext = createContext(); export default AppContext;
appReducer.js
const AppReducer = (state, action) => { switch (action.type) { case 'SET_APP_STATE': return { ...state, openCities: [...action.payload.openCities], }; default: return { ...state, }; } }; export default AppReducer;
appState.js
import React, { useMemo, useReducer } from 'react'; import AppContext from './appContext'; import AppReducer from './appReducer'; const AppState = (props) => { const initialState = { openCities: [], }; const [state, dispatch] = useReducer(AppReducer, initialState); async function setAppState(payload) { dispatch({ type: 'SET_APP_STATE', payload, }); } return ( <AppContext.Provider value={{ openCities: state?.openCities, setAppState, }} > {props.children} </AppContext.Provider> ); }; export default AppState;
setOpenCitiesContext.js
import { useContext, useEffect } from 'react'; import AppContext from '../context/appContext/appContext'; import getOpenCities from '../services/city/getOpenCities'; const setOpenCitiesContext = () => { const { setAppState } = useContext(AppContext); useEffect(() => { fetchOpenCities(); }, []); const fetchOpenCities = async () => { const openCities = await getOpenCities(); setAppState(openCities.cities); }; }; export default setOpenCitiesContext;
app.js
import React, { useEffect } from 'react'; import TagManager from 'react-gtm-module'; import '../font/font.css'; import '../styles/globals.css'; import '../fonts/fonts.css'; import '../styles/custom.css'; import { CookiesProvider } from 'react-cookie'; import AppState from '../context/appContext/appState'; import UserState from '../context/UserState'; import Amplify from 'aws-amplify'; import awsconfig from '../public/aws-exports'; import NProgress from 'nprogress'; import Router from 'next/router'; import 'nprogress/nprogress.css'; import { GiveawayBanner } from '../components/staticStorefront/GiveawayBanner'; import setOpenCitiesContext from '../utils/setOpenCitiesContext'; Amplify.configure(awsconfig); const tagManagerArgs = { gtmId: 'GTM-KBTCTKD', }; function MyApp({ Component, pageProps }) { setOpenCitiesContext(); NProgress.configure({ minimum: 0.3, easing: 'ease', speed: 800, showSpinner: false, }); Router.events.on('routeChangeStart', () => NProgress.start()); Router.events.on('routeChangeComplete', () => NProgress.done()); Router.events.on('routeChangeError', () => NProgress.done()); useEffect(() => { TagManager.initialize(tagManagerArgs); }, []); return ( <CookiesProvider> <AppState> <UserState> {/* <GiveawayBanner /> */} <Component {...pageProps} /> </UserState> </AppState> </CookiesProvider> ); }
问题根源
- 调用时机错误:在
MyApp组件顶层直接调用setOpenCitiesContext时,组件还未渲染到AppStateProvider内部,useContext(AppContext)会返回默认值(创建Context时未传默认值,因此是undefined)。 - 违反Hook规则:
setOpenCitiesContext使用了React Hook,但命名未以use开头,也未在组件或其他自定义Hook中调用,不符合React Hook的使用规范。
修复步骤
方案一:修正自定义Hook并在Provider内调用
- 重命名并修正Hook文件(
useSetOpenCitiesContext.js):
import { useContext, useEffect } from 'react'; import AppContext from '../context/appContext/appContext'; import getOpenCities from '../services/city/getOpenCities'; // 自定义Hook必须以use开头 const useSetOpenCitiesContext = () => { const { setAppState } = useContext(AppContext); useEffect(() => { const fetchOpenCities = async () => { const openCities = await getOpenCities(); // 匹配Reducer期望的payload结构 setAppState({ openCities: openCities.cities }); }; fetchOpenCities(); }, [setAppState]); }; export default useSetOpenCitiesContext;
- 在
app.js的Provider范围内添加初始化组件:
// app.js 修改部分 import useSetOpenCitiesContext from '../utils/useSetOpenCitiesContext'; function MyApp({ Component, pageProps }) { // 移除原有的setOpenCitiesContext()调用 NProgress.configure({ minimum: 0.3, easing: 'ease', speed: 800, showSpinner: false, }); Router.events.on('routeChangeStart', () => NProgress.start()); Router.events.on('routeChangeComplete', () => NProgress.done()); Router.events.on('routeChangeError', () => NProgress.done()); useEffect(() => { TagManager.initialize(tagManagerArgs); }, []); return ( <CookiesProvider> <AppState> <UserState> {/* 在Provider内部调用Hook */} <InitializeOpenCities /> <Component {...pageProps} /> </UserState> </AppState> </CookiesProvider> ); } // 专门用于初始化的组件 function InitializeOpenCities() { useSetOpenCitiesContext(); return null; }
方案二:在AppState内部封装初始化逻辑
直接在appState.js中添加数据初始化逻辑,更符合Context的封装原则:
// appState.js 修改部分 import React, { useReducer, useEffect } from 'react'; import AppContext from './appContext'; import AppReducer from './appReducer'; import getOpenCities from '../services/city/getOpenCities'; const AppState = (props) => { const initialState = { openCities: [], }; const [state, dispatch] = useReducer(AppReducer, initialState); async function setAppState(payload) { dispatch({ type: 'SET_APP_STATE', payload, }); } // 在Context内部初始化数据 useEffect(() => { const fetchOpenCities = async () => { const openCities = await getOpenCities(); setAppState({ openCities: openCities.cities }); }; fetchOpenCities(); }, []); return ( <AppContext.Provider value={{ openCities: state?.openCities, setAppState, }} > {props.children} </AppContext.Provider> ); }; export default AppState;
修改后,useContext(AppContext)就能正确获取Provider提供的setAppState,不会再出现undefined错误。
内容的提问来源于stack exchange,提问作者Quinten Aiton
相关产品推荐
相关产品推荐

