Skip to content

undefined is not an object (evaluating '_api...injectEndpoints') #2817

@imjamescrain

Description

@imjamescrain

Hey guys, I hate asking another "undefined is not an object" question related to RTK, but I'm stuck. The error I'm getting is: undefined is not an object (evaluating '_api.gravyApi.injectEndpoints'). I've tried looking for all circular dependencies, but I'm not seeing any. Any help would be greatly appreciated! Thx in advance.

For context, this was a legacy Redux project that we have started integrating RTK into. Libs are:

"react": "18.1.0"
"react-native": "0.70.1" (not using Fabric)
"@reduxjs/toolkit": "^1.8.5"
"react-redux": "^8.0.4"
"typescript": "^4.8.4"

Details

I have a userSlice file that is importing the userApi so that I can add a builder.addMatcher in the extraReducers that listens for userApi.endpoints.getUser.matchFulfilled.

If I remove this extraReducer related code, I don't have any issues, but if I leave it, I get the error (see HERE in code below).

In fact, the same is true if I try to add a matcher for any of my api's other than the main api. Oddly enough, if I were to do something like gravyApi.endpoints.getApplicationSettings.matchFulfilled it would work.

userApi

import {User} from '../../types';
import {getTimeZone} from 'react-native-localize';
import {gravyApi, getValidationStatus} from './api';

type UpdateUserArgs = {
  user: Partial<User>;
  token: string;
};

export const userApi = gravyApi.injectEndpoints({
  endpoints: builder => ({
    getUser: builder.query({
      query: id => ({
        url: `/users/${id}`,
        validateStatus: getValidationStatus,
      }),
      providesTags: ['User'],
    }),
    updateUser: builder.mutation<User, UpdateUserArgs>({
      query: ({user, token}) => {
        const zone = getTimeZone();
        return {
          url: `/users/${user.id}`,
          method: 'PUT',
          body: {
            user: {
              ...user,
              time_zone: zone,
            },
          },
          validateStatus: getValidationStatus,
        };
      },
      invalidatesTags: ['User'],
    }),
  }),
});

export const {useGetUserQuery, useLazyGetUserQuery, useUpdateUserMutation} =
  userApi;

userSlice

import {User} from '../../types';
import {userApi} from '../apis/userApi';
import {createSlice, PayloadAction} from '@reduxjs/toolkit';

type SliceState = User | null;

const initialState: SliceState = null;

const slice = createSlice({
  name: 'user',
  initialState: initialState,
  reducers: {
    signOut: () => initialState,
    setUser(state, action) {
      let val = null;
      if (action.payload) {
        val = {...action.payload};
        ...
      }
      return val;
    },
    setPremiumUser: (state: SliceState, action: PayloadAction<boolean>) => {
      if (state) {
        state.premium_user = action.payload;
      }
    },
    removeReferringLoanOfficer: (state: SliceState) => {
      if (state) {
        state.referring_loan_officer = null;
      }
    },
  },
  // HERE ...
  // Uncommeting breaks the build
  // extraReducers: builder => {
  //   builder.addMatcher(
  //     userApi.endpoints.getUser.matchFulfilled,
  //     (state, action) => {
  //       return action.payload as any;
  //     },
  //   );
  // },
});

export const {setPremiumUser, removeReferringLoanOfficer, setUser} =
  slice.actions;
export default slice.reducer;

api

And for context, in case it helps.

import {RootState} from '../store';
import {Platform} from 'react-native';
import Config from 'react-native-config';
import {logOutUser} from '../actions/userActions';
import {createApi, fetchBaseQuery} from '@reduxjs/toolkit/query/react';
import {nativeApplicationVersion, nativeBuildVersion} from 'expo-application';

//#region Types

type ApplicationSettings = {
  message?: string;
  ios_minimum_build?: string;
  android_minimum_build?: string;
  savings_interest_rate?: string;
};

type BaseQueryType = ReturnType<typeof fetchBaseQuery>;

//#endregion

//#region Helpers

const getBaseQuery = (): BaseQueryType => {
  return fetchBaseQuery({
    baseUrl: Config.API,
    prepareHeaders: (headers, {getState}) => {
      const token = (getState() as RootState).userToken;
      if (token) {
        headers.set('Authorization', `Bearer ${token}`);
      }
      headers.set('Accept', 'application/json');
      headers.set('Content-Type', 'application/json');
      ...
      return headers;
    },
  });
};

export const getValidationStatus = (
  response: Response,
  result: any,
): boolean => {
  const status = response.status;
  if (!result?.isError) {
    if (status === 200 || status === 201 || status === 204) {
      return true;
    }
  }
  return false;
};

const baseQueryWithReauth: (baseQuery: BaseQueryType) => BaseQueryType =
  baseQuery => async (args, api, extraOptions) => {
    let result = await baseQuery(args, api, extraOptions);
    if (result.error && result.error.originalStatus === 401) {
      api.dispatch(logOutUser());
    }
    return result;
  };

//#endregion

//#region Core Apis

export const gravyApi = createApi({
  reducerPath: 'gravyApi',
  tagTypes: ['ApplicationSettings', 'DirectDeposit', 'Courses', 'User'],
  baseQuery: baseQueryWithReauth(getBaseQuery()),
  endpoints: builder => ({
    postNewSession: builder.mutation({
      async queryFn(args, queryApi, extraOptions, baseQuery) {
        try {
          const result = await baseQuery({
            url: '/sessions',
            method: 'POST',
            body: {
              user: {
                mobile_phone_number: args.phone,
                password: args.password,
              },
            },
          });
          if (result) {
            let mfaAvailable = false;
            const status = result.meta?.response?.status;
            switch (status) {
              ...
            }
          }
          throw new Error('Something went wrong.');
        } catch (e: any) {
          return {error: e.message};
        }
      },
    }),
    getApplicationSettings: builder.query<ApplicationSettings, void>({
      query: () => ({
        url: '/application_settings',
        providesTags: ['ApplicationSettings'],
        validateStatus: getValidationStatus,
      }),
    }),
  }),
});

//#endregion

// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const {useGetApplicationSettingsQuery, usePostNewSessionMutation} =
  gravyApi;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions