Redux Thunk中间件报错:根Reducer未定义问题求助
Redux整合MSAL时"Expected root reducer to be a function"错误排查
我在应用中使用MSAL-React实现OAuth(微软身份验证以启用SSO),希望将MSAL获取的accessToken存入Redux实现跨页面访问,但每次需刷新多次才能从Redux Store获取token。尝试用Redux Thunk中间件解决时,持续报错:
WebpackError: Expected the root reducer to be a function. Instead, received: 'undefined'
问题代码
store.js
import { createStore, applyMiddleware } from 'redux' import thunkMiddleware from 'redux-thunk' import { composeWithDevTools } from 'redux-devtools-extension' import rootReducer from './rootReducer' const composedEnhancer = composeWithDevTools(applyMiddleware(thunkMiddleware)) const store = createStore(rootReducer, composedEnhancer) export default store
rootReducer.js
import { fetchUsersToken, reducer } from "./reducer"; import { combineReducers } from "redux"; // 之前写法是export const rootReducer = combineReducers({...}) export const rootReducer = () => combineReducers({ reduxState: reducer });
reducer.js
import { useMsal } from "@azure/msal-react"; import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { loginRequest } from "./authConfig"; import { msalInstance } from "./pages"; import * as actionTypes from "./action-types"; const initialState = [] export default function reducer(state = initialState, action) { switch (action.type) { // omit other reducer cases case actionTypes.GETTOKEN: { return action.payload } default: return state } } export const fetchUsersToken = createAsyncThunk("users/fetchUsersToken", async (dispatch, getState) => { const {accounts} = useMsal() const request = { ...loginRequest, account: accounts[0], } const tkn = await msalInstance.acquireTokenSilent(request) dispatch({ type: actionTypes.GETTOKEN, payload: tkn.accessToken }) });
index.js
import React from "react" import { Helmet } from "react-helmet" import { PublicClientApplication } from "@azure/msal-browser" import { MsalProvider } from "@azure/msal-react" import { msalConfig } from "../authConfig" import PageLayout from "./PageLayout" import { createTheme, ThemeProvider } from "@mui/material/styles" //Redux import { Provider } from "react-redux"; import {store} from "../store"; store.dispatch(fetchUsersToken()); //Redux Ends here export const msalInstance = new PublicClientApplication(msalConfig) const theme = createTheme({ palette: { primary: { main: "#000000" }, }, }) export default function Home() { return ( <> <Helmet> <title>XXXXXXXXXX</title> </Helmet> <MsalProvider instance={msalInstance}> <ThemeProvider theme={theme}> <Provider store={store}> <PageLayout /> </Provider> </ThemeProvider> </MsalProvider> </> ) }
问题根源及修复方案
1. RootReducer定义错误
createStore要求传入的rootReducer必须是一个函数,但你的代码将rootReducer定义为返回combineReducers结果的函数,导致传入createStore时实际是一个未执行的函数引用,而非真正的reducer函数。
修复后的rootReducer.js:
import { reducer } from "./reducer"; import { combineReducers } from "redux"; // 直接导出combineReducers的结果,而非包裹在函数中 export const rootReducer = combineReducers({ reduxState: reducer });
2. AsyncThunk写法错误
createAsyncThunk的回调函数参数格式错误:第一个参数是传入的payload,第二个是包含dispatch/getState的thunkAPI对象,而非直接传入dispatch和getState- 不能在非React组件/自定义Hook中使用
useMsal,需直接通过msalInstance获取账户信息 createAsyncThunk会自动分发pending/fulfilled/rejected类型的action,无需手动dispatch自定义action
修复后的reducer.js:
import { createAsyncThunk } from "@reduxjs/toolkit"; import { loginRequest } from "./authConfig"; import { msalInstance } from "./pages"; import * as actionTypes from "./action-types"; // token是字符串,初始值改为空字符串更合理 const initialState = ""; export default function reducer(state = initialState, action) { switch (action.type) { case actionTypes.GETTOKEN: { return action.payload; } // 可选:处理createAsyncThunk自动生成的fulfilled action case "users/fetchUsersToken/fulfilled": { return action.payload; } default: return state; } } export const fetchUsersToken = createAsyncThunk("users/fetchUsersToken", async (_, thunkAPI) => { const accounts = msalInstance.getAllAccounts(); if (!accounts.length) { throw new Error("未找到登录账户"); } const request = { ...loginRequest, account: accounts[0], }; const tkn = await msalInstance.acquireTokenSilent(request); // 返回token,createAsyncThunk会自动分发fulfilled action return tkn.accessToken; });
3. Index.js中的初始化错误
store导入方式错误:store.js是export default,需用import store from "../store"而非import {store}dispatch时机错误:msalInstance在dispatch之后才创建,会导致msalInstance为undefined;且不能在组件外部直接调用store.dispatch,需在组件内部通过useDispatch和useEffect触发- 将token获取逻辑封装在组件内部,确保MSAL实例已初始化
修复后的index.js:
import React, { useEffect } from "react" import { Helmet } from "react-helmet" import { PublicClientApplication } from "@azure/msal-browser" import { MsalProvider } from "@azure/msal-react" import { msalConfig } from "../authConfig" import PageLayout from "./PageLayout" import { createTheme, ThemeProvider } from "@mui/material/styles" //Redux import { Provider, useDispatch } from "react-redux"; import store from "../store"; import { fetchUsersToken } from "../reducer"; // 导入fetchUsersToken // 先初始化MSAL实例 export const msalInstance = new PublicClientApplication(msalConfig) const theme = createTheme({ palette: { primary: { main: "#000000" }, }, }) // 封装组件处理token获取 const AppWrapper = () => { const dispatch = useDispatch(); useEffect(() => { // 组件挂载后触发token获取 dispatch(fetchUsersToken()).catch(err => { console.error("获取token失败:", err); // 可选:处理token获取失败逻辑,比如跳转到登录页 }); }, [dispatch]); return <PageLayout />; } export default function Home() { return ( <> <Helmet> <title>XXXXXXXXXX</title> </Helmet> <MsalProvider instance={msalInstance}> <ThemeProvider theme={theme}> <Provider store={store}> <AppWrapper /> </Provider> </ThemeProvider> </MsalProvider> </> ) }
关键修复总结
- 修正rootReducer的导出方式,直接返回
combineReducers结果 - 调整AsyncThunk的参数结构,移除React Hook的不当使用
- 确保MSAL实例先于Redux dispatch初始化,在组件内部通过
useEffect触发token获取 - 适配reducer初始状态类型为字符串,匹配token的数据格式
内容的提问来源于stack exchange,提问作者Lakshmi
相关产品推荐
相关产品推荐

