streamich/react-use · error · Error

useReducerContext must be used inside a ReducerProvider.

Error message

useReducerContext must be used inside a ReducerProvider.

What it means

Thrown by the hook returned from createReducerContext when it is called outside a matching <ReducerProvider>. The factory creates a React context defaulting to undefined; useReducerContext reads it via useContext and, when the value is null or undefined (== null), concludes no provider is supplying the [state, dispatch] tuple and throws. This is a structural wiring error, not a data error — the hook simply has no reducer state to read.

Source

Thrown at src/factory/createReducerContext.ts:30

  const ReducerProvider = ({
    children,
    initialState,
  }: {
    children?: React.ReactNode;
    initialState?: React.ReducerState<R>;
  }) => {
    const state = useReducer<R>(
      reducer,
      initialState !== undefined ? initialState : defaultInitialState
    );
    return providerFactory({ value: state }, children);
  };

  const useReducerContext = () => {
    const state = useContext(context);
    if (state == null) {
      throw new Error(`useReducerContext must be used inside a ReducerProvider.`);
    }
    return state;
  };

  return [useReducerContext, ReducerProvider, context] as const;
};

export default createReducerContext;

View on GitHub (pinned to fbe99c6327)

Solutions

  1. Wrap the component subtree that calls useReducerContext in the <ReducerProvider> returned from the same createReducerContext call: <ReducerProvider><MyComponent/></ReducerProvider>.
  2. Verify you are using the hook and Provider from the SAME factory invocation — call createReducerContext once, export both members, and import both together; do not call the factory twice and mix the pairs.
  3. In tests/SSR, mount the consumer inside the provider (e.g. render(<ReducerProvider><Comp/></ReducerProvider>) in @testing-library) instead of rendering <Comp/> bare.
  4. If a portal or lazy boundary is involved, ensure the provider sits above the point where the portal/lazy tree resolves, or lift the provider to the root.

Example fix

// before
const [useReducerContext, ReducerProvider] = createReducerContext(reducer, init);
function Child() {
  const [state, dispatch] = useReducerContext(); // throws: no provider above
  return <p>{state}</p>;
}
export default function App() {
  return <Child/>;
}

// after
export default function App() {
  return (
    <ReducerProvider>
      <Child/>
    </ReducerProvider>
  );
}
Defensive patterns

Strategy: validation

Validate before calling

// Structural: there is no pre-call probe for a context hook, so the
// 'validation' is to render consumers only inside their provider.
// Co-locate creation + provider + hook, and assert the tree shape:
//
// factory.ts
export const [useReducerContext, ReducerProvider] = createReducerContext(reducer, init);

// App.tsx — provider MUST wrap every consumer
<ReducerProvider>
  <ConsumesReducer/>
</ReducerProvider>

Type guard

// A context-consumer type guard: narrow a value to the [state, dispatch] tuple
// before reading it, so you never call the hook on an unmounted/unprovided tree.
const isReducerTuple = <S, A>(
  v: unknown
): v is [S, React.Dispatch<A>] =>
  Array.isArray(v) && v.length === 2 && typeof v[1] === 'function';

// Note: the hook itself throws before you can apply this; the real guard is the
// provider in the tree (see validationCode).

Try / catch

// Wrap the consumer subtree in an error boundary so a missing-provider throw
// surfaces a clear fallback instead of blanking the screen.
class ProviderBoundary extends React.Component<
  { children: React.ReactNode; fallback: React.ReactNode },
  { hasError: boolean }
> {
  state = { hasError: false };
  static getDerivedStateFromError() { return { hasError: true }; }
  render() {
    return this.state.hasError ? this.props.fallback : this.props.children;
  }
}
// usage: <ProviderBoundary fallback={<p>ReducerProvider missing</p>}><Consumes/></ProviderBoundary>

Prevention

When it happens

Trigger: Calling the useReducerContext hook (first element of the tuple returned by createReducerContext(reducer, defaultInitialState)) in a component that is not a descendant of the corresponding <ReducerProvider>. Also triggered if the wrong provider instance is used (e.g. calling createReducerContext twice and using hook A inside provider B, since each call makes a distinct context), or when a provider is conditionally rendered such that the consumer mounts outside it.

Common situations: Forgetting to wrap the app/subtree in <ReducerProvider>; creating the context at module scope but rendering the consumer in a sibling/portal that escapes the provider; copy-pasting the hook into a component defined in another file that was never wrapped; SSR/test renders that mount the consumer in isolation without the provider.

Related errors


AI-assisted analysis of streamich/react-use@fbe99c6327 (2026-08-12). Data as JSON: /api/errors/b1e8880d74af8831. Report an issue: GitHub.