You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,
});

错误原因分析

  1. 核心错误:invalidatesTags位置错误
    在authApiSlice中,invalidatesTags: ["users"]被放在了endpoints对象的顶级,而不是login mutation的配置内部。RTK Query无法识别这个位置的配置,导致处理mutation时找不到对应的endpoint定义,触发definitions[meta.arg.endpointName]为undefined的错误。

  2. 次要错误:拼写错误
    在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 07:20:26