Search code examples
reduxredux-toolkitrtk-queryredux-reducers

RTK Query extraReducers: Uncaught TypeError: Cannot read properties of undefined (reading 'matchFulfilled')


I am using RTK & RTK Query for state management and data fetching in my app. My store is set up via code-splitting and I get the following error related to my extraReducer defined in storeNew/authSliceNew.js at the initial loading of the app Uncaught TypeError: Cannot read property 'matchFulfilled' of undefined

What I realized so far is that if I comment out my extraReducers in storeNew/authSliceNew.js, re-load the app, and then uncomment them, they seem to be working. But if I have them uncommented right from the start, I get the error. I tried to find a solution for a couple of hours by now, but could not find anything helpful. So I would be glad if anyone could point me to the right direction.

My storeNew/baseApi.js

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

export const baseApi = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: "api/" }),
  endpoints: () => ({}),
  tagTypes: ["User", "Budget"],
});

my 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;

my 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;

my 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;

Solution

  • If you access baseApi.endpoints.registerUser.matchFulfilled here, you have no guarantee that the registerUser endpoint has already been injected at that point in time (meaning: that the file injecting it has been included yet).

    Export authApi instead, and use authApi.endpoints.registerUser.matchFulfilled.

    authApi is the same object as baseApi (literally, a reference to the same object!), but at the time authApi is available to you, you have a guarantee that the injectEndpoints call for registerUser has been made.