RTK Query extraReducers初始化报错:Cannot read 'matchFulfilled'
问题描述
使用RTK与RTK Query做状态管理和数据获取,采用代码分割配置store时,应用初始化加载阶段,storeNew/authSliceNew.js中的extraReducers抛出错误:
Uncaught TypeError: Cannot read properties of undefined (reading 'matchFulfilled')
临时 workaround:先注释authSliceNew.js里的extraReducers,启动应用后再取消注释,功能就能正常运行;但初始不注释就会触发上述报错。
相关代码文件:
storeNew/baseApi.js
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; export const baseApi = createApi({ baseQuery: fetchBaseQuery({ baseUrl: "api/" }), endpoints: () => ({}), tagTypes: ["User", "Budget"], });
storeNew/authApiNew.js
import { baseApi } from "./baseApi"; const authApi = baseApi.injectEndpoints({ endpoints: (builder) => ({ registerUser: builder.mutation({ query: (registerCredentials) => ({ url: "account/register", method: "POST", body: { ...registerCredentials }, }), }), }), overrideExisting: false, }); export const { useRegisterUserMutation, } = authApi;
storeNew/authSliceNew.js
import { createSlice } from "@reduxjs/toolkit"; import { baseApi } from "./baseApi"; const initialState = { isAuthenticated: false, register_success: false, feedback: null, feedbackType: null, }; const authSlice = createSlice({ name: "auth", initialState: initialState, reducers: { resetFeedback: (state) => { state.feedback = null; state.feedbackType = null; }, resetRegisterSuccess: (state) => { state.register_success = false; }, }, extraReducers: (builder) => { builder.addMatcher( baseApi.endpoints.registerUser.matchFulfilled, (state) => { state.register_success = true; state.feedback = null; state.feedbackType = null; } ); }, }); export const authActions = authSlice.actions; export default authSlice;
storeNew/index.js
import { configureStore } from "@reduxjs/toolkit"; import { setupListeners } from "@reduxjs/toolkit/query"; import { baseApi } from "./baseApi"; import authSliceNew from "./authSliceNew"; const store = configureStore({ reducer: { [baseApi.reducerPath]: baseApi.reducer, authNew: authSliceNew.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(baseApi.middleware), }); setupListeners(store.dispatch); export default store;
解决方案
这个问题是模块加载顺序导致的初始化时机问题:
authSliceNew.js直接从baseApi取registerUserendpoint,但这个endpoint是在authApiNew.js中注入到baseApi的- 初始化时,
store/index.js先加载baseApi,再加载authSliceNew,此时authApiNew还没执行,baseApi.endpoints.registerUser还不存在,所以访问matchFulfilled会报错
推荐两种解决方法:
方法1:从authApiNew导出endpoint引用
修改authApiNew.js,把注入后的endpoint单独导出:
import { baseApi } from "./baseApi"; const authApi = baseApi.injectEndpoints({ endpoints: (builder) => ({ registerUser: builder.mutation({ query: (registerCredentials) => ({ url: "account/register", method: "POST", body: { ...registerCredentials }, }), }), }), overrideExisting: false, }); export const { useRegisterUserMutation } = authApi; // 新增:导出registerUser endpoint export const { registerUser } = authApi.endpoints;
修改authSliceNew.js,导入这个导出的endpoint,而非从baseApi获取:
import { createSlice } from "@reduxjs/toolkit"; // 替换导入源,从authApiNew拿registerUser endpoint import { registerUser } from "./authApiNew"; const initialState = { isAuthenticated: false, register_success: false, feedback: null, feedbackType: null, }; const authSlice = createSlice({ name: "auth", initialState: initialState, reducers: { resetFeedback: (state) => { state.feedback = null; state.feedbackType = null; }, resetRegisterSuccess: (state) => { state.register_success = false; }, }, extraReducers: (builder) => { builder.addMatcher( registerUser.matchFulfilled, // 直接用导出的endpoint (state) => { state.register_success = true; state.feedback = null; state.feedbackType = null; } ); }, }); export const authActions = authSlice.actions; export default authSlice;
最后在store/index.js中提前加载authApiNew,确保endpoint注入完成:
import { configureStore } from "@reduxjs/toolkit"; import { setupListeners } from "@reduxjs/toolkit/query"; import { baseApi } from "./baseApi"; // 提前导入authApiNew,保证endpoint先注入到baseApi import "./authApiNew"; import authSliceNew from "./authSliceNew"; const store = configureStore({ reducer: { [baseApi.reducerPath]: baseApi.reducer, authNew: authSliceNew.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(baseApi.middleware), }); setupListeners(store.dispatch); export default store;
方法2:用RTK Query内置方法替代extraReducers
如果不需要在auth slice里维护状态,可以直接在mutation中处理逻辑,避免监听endpoint状态:
// authApiNew.js import { baseApi } from "./baseApi"; import { authActions } from "./authSliceNew"; const authApi = baseApi.injectEndpoints({ endpoints: (builder) => ({ registerUser: builder.mutation({ query: (registerCredentials) => ({ url: "account/register", method: "POST", body: { ...registerCredentials }, }), // 使用onQueryStarted触发slice的action async onQueryStarted(args, { dispatch, queryFulfilled }) { try { await queryFulfilled; dispatch(authActions.resetFeedback()); // 这里可以直接dispatch更新auth状态的action } catch (error) { // 处理注册失败逻辑 } }, }), }), overrideExisting: false, }); export const { useRegisterUserMutation } = authApi;
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

