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

React useContext报对象不可迭代Symbol.iterator读取错误

问题原因

报错是三个问题叠加导致的:

  1. 你写的defaultContext函数每次传null调用时,都会执行createContext({})创建一个全新的Context对象。父组件里Provider用的Context、子组件里调用useContext拿的Context是完全独立的两个实例,子组件根本读不到Provider注入的值,只能拿到Context初始化的空对象{}。
  2. 你往Context里塞的value是对象格式{ state, dispatch },子组件却用数组写法const [state, dispatch] = useContext(...)解构,空对象本身没有迭代器,执行数组解构直接触发「object is not iterable」报错。
  3. 你用useMemo包裹Provider组件时传了空依赖数组,后续state更新时Provider不会重渲染,就算Context实例对了,子组件也拿不到最新的状态值。
修复方案

直接改通用Provider的实现,把默认Context全局唯一化,同时封装配套的读取hook,避免手动传Context出错:

1. 重写useGenericReducer.tsx

import React, { useReducer, createContext, useContext } from 'react';

// 全局默认Context,仅初始化一次,保证所有调用方拿到的是同一个实例
const globalDefaultContext = createContext<{
  state: any;
  dispatch: React.Dispatch<any>;
} | null>(null);

export function useGenericProvider<State, Action>(
  reducer: React.Reducer<State, Action>,
  initialState: State,
  customContext?: React.Context<any>
) {
  const activeContext = customContext ?? globalDefaultContext;
  const [state, dispatch] = useReducer(reducer, initialState);

  // 不要用useMemo缓存组件,直接返回Provider组件,保证state变化时能正常重渲染
  const Provider = ({ children }: { children: React.ReactNode }) => {
    return (
      <activeContext.Provider value={{ state, dispatch }}>
        {children}
      </activeContext.Provider>
    );
  };

  // 封装配套的读取hook,避免用户手动找Context实例传错
  const useProviderContext = () => {
    const ctxValue = useContext(activeContext);
    if (!ctxValue) {
      throw new Error('useProviderContext must be used inside the corresponding Provider');
    }
    return ctxValue;
  };

  return {
    Provider,
    useProviderContext,
    context: activeContext
  };
}

2. 修改Child.tsx

直接用hook返回的useProviderContext读取值,不要自己调用defaultContext创建新实例,同时用对象格式解构:

import React from 'react';
import { useGenericProvider } from './useGenericReducer';

// 复用全局默认Context对应的读取hook,自定义Context场景可以自己传Context给useGenericProvider生成对应hook
const { useProviderContext } = useGenericProvider(
  (state: {count: number}, action: any) => state,
  { count: 0 }
);

const Child = () => {
  const { state, dispatch } = useProviderContext();
  return (
    <div>
      <div>当前计数:{state.count}</div>
      <button onClick={() => dispatch('ADD')}>加1</button>
      <button onClick={() => dispatch('SUB')}>减1</button>
    </div>
  );
};

export default Child;

3. Parent.tsx基本无需改动

正常调用useGenericProvider拿到Provider包裹子组件即可,需要用自定义Context时把自己创建的Context作为第三个参数传入就行:

import React from 'react';
import { render } from 'react-dom';
import { useGenericProvider } from './useGenericReducer';
import Child from './Child';

interface IInitState {
  count: number;
}

const App: React.FC = () => {
  const initState: IInitState = { count: 0 };
  function reducer(state: IInitState, action: any) {
    switch (action) {
      case 'ADD':
        return { ...state, count: state.count + 1 };
      case 'SUB':
        return { ...state, count: state.count - 1 };
      default:
        return state;
    }
  }

  const { Provider } = useGenericProvider(reducer, initState);
  return (
    <Provider>
      <Child />
    </Provider>
  );
};

render(<App />, document.getElementById('root'));
修复点说明
  • 默认Context全局只初始化一次,从根源避免多实例导致读不到值的问题。
  • 去掉了错误的空依赖useMemo缓存Provider的写法,保证状态更新时组件正常重渲染。
  • 封装了配套的读取hook,统一解构格式,避免手动调用useContext时传错实例、解构格式不对的问题。
  • 保留自定义Context传参能力,用户需要隔离Context场景时可以传入自己创建的Context实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 18:51:47