{"record":{"id":"b1e8880d74af8831","repo":"streamich/react-use","slug":"usereducercontext-must-be-used-inside-a-reducerpro","errorCode":null,"errorMessage":"useReducerContext must be used inside a ReducerProvider.","messagePattern":"useReducerContext must be used inside a ReducerProvider\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/factory/createReducerContext.ts","lineNumber":30,"sourceCode":"\n  const ReducerProvider = ({\n    children,\n    initialState,\n  }: {\n    children?: React.ReactNode;\n    initialState?: React.ReducerState<R>;\n  }) => {\n    const state = useReducer<R>(\n      reducer,\n      initialState !== undefined ? initialState : defaultInitialState\n    );\n    return providerFactory({ value: state }, children);\n  };\n\n  const useReducerContext = () => {\n    const state = useContext(context);\n    if (state == null) {\n      throw new Error(`useReducerContext must be used inside a ReducerProvider.`);\n    }\n    return state;\n  };\n\n  return [useReducerContext, ReducerProvider, context] as const;\n};\n\nexport default createReducerContext;\n","sourceCodeStart":12,"sourceCodeEnd":39,"githubUrl":"https://github.com/streamich/react-use/blob/fbe99c6327e6af94df03bc8bd6ecc5e3ff04fbcc/src/factory/createReducerContext.ts#L12-L39","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap the component subtree that calls useReducerContext in the <ReducerProvider> returned from the same createReducerContext call: <ReducerProvider><MyComponent/></ReducerProvider>.","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.","In tests/SSR, mount the consumer inside the provider (e.g. render(<ReducerProvider><Comp/></ReducerProvider>) in @testing-library) instead of rendering <Comp/> bare.","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."],"exampleFix":"// before\nconst [useReducerContext, ReducerProvider] = createReducerContext(reducer, init);\nfunction Child() {\n  const [state, dispatch] = useReducerContext(); // throws: no provider above\n  return <p>{state}</p>;\n}\nexport default function App() {\n  return <Child/>;\n}\n\n// after\nexport default function App() {\n  return (\n    <ReducerProvider>\n      <Child/>\n    </ReducerProvider>\n  );\n}","handlingStrategy":"validation","validationCode":"// Structural: there is no pre-call probe for a context hook, so the\n// 'validation' is to render consumers only inside their provider.\n// Co-locate creation + provider + hook, and assert the tree shape:\n//\n// factory.ts\nexport const [useReducerContext, ReducerProvider] = createReducerContext(reducer, init);\n\n// App.tsx — provider MUST wrap every consumer\n<ReducerProvider>\n  <ConsumesReducer/>\n</ReducerProvider>","typeGuard":"// A context-consumer type guard: narrow a value to the [state, dispatch] tuple\n// before reading it, so you never call the hook on an unmounted/unprovided tree.\nconst isReducerTuple = <S, A>(\n  v: unknown\n): v is [S, React.Dispatch<A>] =>\n  Array.isArray(v) && v.length === 2 && typeof v[1] === 'function';\n\n// Note: the hook itself throws before you can apply this; the real guard is the\n// provider in the tree (see validationCode).","tryCatchPattern":"// Wrap the consumer subtree in an error boundary so a missing-provider throw\n// surfaces a clear fallback instead of blanking the screen.\nclass ProviderBoundary extends React.Component<\n  { children: React.ReactNode; fallback: React.ReactNode },\n  { hasError: boolean }\n> {\n  state = { hasError: false };\n  static getDerivedStateFromError() { return { hasError: true }; }\n  render() {\n    return this.state.hasError ? this.props.fallback : this.props.children;\n  }\n}\n// usage: <ProviderBoundary fallback={<p>ReducerProvider missing</p>}><Consumes/></ProviderBoundary>","preventionTips":["Call createReducerContext once at module scope and export the hook and Provider together so they can never diverge.","Keep a single root provider high in the tree (above any lazy/portal boundaries) that contains all consumers.","Add an ESLint rule or code review check that any file importing useReducerContext also renders within ReducerProvider.","In tests, always mount consumers inside the provider via a shared test util."],"tags":["react","context","hooks","provider","wiring"],"backgroundTag":null,"analyzedSha":"fbe99c6327e6af94df03bc8bd6ecc5e3ff04fbcc","analyzedAt":"2026-08-12T19:24:06.802Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}