Redux Toolkit Query报错:无法解构'merge'属性,求排查方案
问题:Redux Toolkit搭建JWT认证Store时出现TypeError错误
运行时错误信息:
Uncaught (in promise) TypeError: Cannot destructure property 'merge' of 'definitions[meta.arg.endpointName]' as it is undefined. at @reduxjs_toolkit_query_react.js?v=086dfd1c:999:13 at updateQuerySubstateIfExists (@reduxjs_toolkit_query_react.js?v=086dfd1c:909:5) at @reduxjs_toolkit_query_react.js?v=086dfd1c:995:9 at chunk-CMRBLURP.js?v=086dfd1c:2347:20 at Immer2.produce (chunk-CMRBLURP.js?v=086dfd1c:904:20) at chunk-CMRBLURP.js?v=086dfd1c:2346:18 at Array.reduce (<anonymous>) at reducer (chunk-CMRBLURP.js?v=086dfd1c:2327:25) at reducer (chunk-CMRBLURP.js?v=086dfd1c:2752:16) at combination (chunk-CMRBLURP.js?v=086dfd1c:302:31)
相关代码配置
authSlice
/* eslint-disable no-unused-vars */ import { createSlice } from '@reduxjs/toolkit'; const authSlice = createSlice({ name: 'auth', initialState: { userData: null, accessToken: null }, reducers: { setCredentials: (state, action) => { console.log("Store ", action.payload); const { userData, accessToken } = action.payload; state.userData = userData; state.accessToken = accessToken; }, logOut: (state, action) => { state.userData = null; state.accessToken = null; } } }); export const { setCredentials, logOut } = authSlice.actions; export default authSlice.reducer; export const selectCurrentUser = state => state.auth.userData; export const selectCurrentToken = state => state.auth.accessToken;
authApiSlice
import { apiSliceUser } from "./apiSliceUser"; export const authApiSlice = apiSliceUser.injectEndpoints({ endpoints: builder => ({ login: builder.mutation({ query: credentials => { console.log(credentials) return { url: '/login', method: 'POST', body: { ...credentials }, } } }), invalidatesTags: ["users"], }) }) export const { useLoginMutation } = authApiSlice;
apiSliceUser
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { logOut, setCredentials } from './authSlice'; //* This unit is responsible for login & Refreshing Token functionality... :) const baseQuery = fetchBaseQuery({ baseUrl: 'http://localhost:3000/users', credentials: 'include', prepareHeaders: (headers, { getState }) => { const token = getState().auth.accessToken; console.log(getState().auth); if (token) { headers.set('authorization', `Bearer ${token}`); } return headers; } }); const baseQueryWithReauth = async (args, api, extraOptions) => { let result = await baseQuery(args, api, extraOptions); console.log("Result : ", result); //! Since the access token expires then try to refresh the token... :) if (result?.error?.originalStatus === 403) { console.log('Sending Refresh Token'); const refreshResult = await baseQuery('/refresh', api, extraOptions); if (refreshResult?.data) { const userData = api.getState().auth.userdata; api.dispatch(setCredentials({ userData, accessToken: refreshResult.accessToken })); result = await baseQuery(args, api, extraOptions); console.log(result); } else { api.dispatch(logOut()) } } return result; } export const apiSliceUser = createApi({ baseQuery: baseQueryWithReauth, tagTypes: ["users"], endpoints: builder => ({ }) })
store
import { configureStore } from '@reduxjs/toolkit'; import postSlice from './Posts/postSlice'; import userSlice from './Users/userSlice'; import { apiSlice } from './Posts/apiSlice'; import { apiSliceUser } from './Authentication/apiSliceUser'; import authReducer from './Authentication/authSlice'; export const store = configureStore({ reducer: { posts: postSlice, user: userSlice, [apiSlice.reducerPath]: apiSlice.reducer, [apiSliceUser.reducerPath]: apiSliceUser.reducer, auth: authReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(apiSlice.middleware).concat(apiSliceUser.middleware), devTools: true, });
错误原因分析
核心错误:
invalidatesTags位置错误
在authApiSlice中,invalidatesTags: ["users"]被放在了endpoints对象的顶级,而不是loginmutation的配置内部。RTK Query无法识别这个位置的配置,导致处理mutation时找不到对应的endpoint定义,触发definitions[meta.arg.endpointName]为undefined的错误。次要错误:拼写错误
在apiSliceUser的baseQueryWithReauth函数中,const userData = api.getState().auth.userdata;里的userdata拼写错误,应该是userData(和authSlice中的状态字段一致),否则会导致获取用户数据为undefined。
修复方案
修正后的authApiSlice代码
import { apiSliceUser } from "./apiSliceUser"; export const authApiSlice = apiSliceUser.injectEndpoints({ endpoints: builder => ({ login: builder.mutation({ query: credentials => { console.log(credentials) return { url: '/login', method: 'POST', body: { ...credentials }, } }, // 将invalidatesTags移到mutation配置内部 invalidatesTags: ["users"] }) }) }) export const { useLoginMutation } = authApiSlice;
修正后的apiSliceUser中拼写错误
// ... 其他代码不变 if (refreshResult?.data) { // 修正拼写:userdata -> userData const userData = api.getState().auth.userData; api.dispatch(setCredentials({ userData, accessToken: refreshResult.data.accessToken })); result = await baseQuery(args, api, extraOptions); console.log(result); } // ... 其他代码不变
学习资源
- Redux Toolkit基础教程:重点掌握
createSlice、configureStore的核心用法,理解Redux状态管理的基本流程 - RTK Query官方指南:深入学习
createApi、injectEndpoints的配置规则,尤其是mutation/query的invalidatesTags、providesTags的正确使用方式 - RTK Query认证示例:学习如何结合JWT实现token刷新、自动重试等认证相关的逻辑
内容的提问来源于stack exchange,提问作者Ashlok Choudhary
相关产品推荐
相关产品推荐

