{"record":{"id":"ceb87a6a89e880a4","repo":"reduxjs/redux","slug":"expected-the-nextreducer-to-be-a-function-instead","errorCode":null,"errorMessage":"Expected the nextReducer to be a function. Instead, received: '${kindOf(nextReducer)}'","messagePattern":"Expected the nextReducer to be a function\\. Instead, received: '(.+?)'","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/createStore.ts","lineNumber":322,"sourceCode":"    const listeners = (currentListeners = nextListeners)\n    listeners.forEach(listener => {\n      listener()\n    })\n    return action\n  }\n\n  /**\n   * Replaces the reducer currently used by the store to calculate the state.\n   *\n   * You might need this if your app implements code splitting and you want to\n   * load some of the reducers dynamically. You might also need this if you\n   * implement a hot reloading mechanism for Redux.\n   *\n   * @param nextReducer The reducer for the store to use instead.\n   */\n  function replaceReducer(nextReducer: Reducer<S, A>): void {\n    if (typeof nextReducer !== 'function') {\n      throw new Error(\n        `Expected the nextReducer to be a function. Instead, received: '${kindOf(\n          nextReducer\n        )}'`\n      )\n    }\n\n    currentReducer = nextReducer as unknown as Reducer<S, A, PreloadedState>\n\n    // This action has a similar effect to ActionTypes.INIT.\n    // Any reducers that existed in both the new and old rootReducer\n    // will receive the previous state. This effectively populates\n    // the new state tree with any relevant data from the old one.\n    dispatch({ type: ActionTypes.REPLACE } as A)\n  }\n\n  /**\n   * Interoperability point for observable/reactive libraries.\n   * @returns A minimal observable of state changes.","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/reduxjs/redux/blob/3084fc33bb23ab4cab4796172eafe342da4e73d9/src/createStore.ts#L304-L340","documentation":"replaceReducer(nextReducer) swaps the active reducer, used for hot-reloading and code-splitting. The new value must be a function so dispatch can call it as (state, action) => state. Passing anything else (undefined, object, partial combineReducers result) would break the dispatch loop, so Redux type-checks up front.","triggerScenarios":"store.replaceReducer(undefined) — a new reducer import resolved to undefined; replaceReducer called with a slice map instead of combineReducers(map); replaceReducer(combineReducers) passing the function reference instead of calling it; circular import making the new reducer undefined at hot-reload time.","commonSituations":"Webpack HMR `module.hot.accept('./reducers', () => store.replaceReducer(require('./reducers').default))` where the default export is missing; code-splitting that injects a slice object instead of a recombined root reducer; refactor that renamed the reducer export.","solutions":["Log `typeof nextReducer` before replaceReducer and confirm it is 'function'.","When re-splitting, rebuild the combined reducer: `store.replaceReducer(combineReducers({ ...newSlices }))`.","In HMR, account for default-vs-namespace exports: `require('./reducers').default` for default, `.rootReducer` for named.","Resolve circular imports before hot-reloading."],"exampleFix":"// before\nif (module.hot) {\n  module.hot.accept('./reducers', () => {\n    store.replaceReducer(require('./reducers'))   // module object, not the reducer fn\n  })\n}\n\n// after\nif (module.hot) {\n  module.hot.accept('./reducers', () => {\n    const nextRootReducer = require('./reducers').default\n    store.replaceReducer(nextRootReducer)\n  })\n}","handlingStrategy":"type-guard","validationCode":"if (typeof nextReducer !== 'function') {\n  throw new TypeError(`nextReducer must be a function, got ${typeof nextReducer}`)\n}\nstore.replaceReducer(nextReducer)","typeGuard":"const isReducer = (x): x is (s:any, a:any)=>any => typeof x === 'function'","tryCatchPattern":null,"preventionTips":["In HMR, account for default-vs-namespace exports when re-requiring reducers.","Rebuild combineReducers when injecting new slices dynamically.","Log typeof nextReducer before replaceReducer.","Avoid circular imports between reducers and the store module."],"tags":["redux","createstore","replacereducer","hot-reload","code-splitting","import"],"backgroundTag":null,"analyzedSha":"3084fc33bb23ab4cab4796172eafe342da4e73d9","analyzedAt":"2026-08-12T13:21:41.727Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}