reduxjs / redux-toolkit

The official, opinionated, batteries-included toolset for efficient Redux development
https://redux-toolkit.js.org
MIT License
10.71k stars 1.17k forks source link

Cannot read properties of undefined (reading 'rtkQuery') #2567

Closed VictorPulzz closed 2 years ago

VictorPulzz commented 2 years ago

Hello, I got this strange error: Cannot read properties of undefined (reading 'rtkQuery') I have a project where the structure is like this:

...
rtk ----
    - modules
          - endpoints.ts
    - rtkQuery.ts
    - index.ts

And I want to make beautiful imports so that both rtkQuery and individual modules with rtkQuery.injectEndpoints are imported in the index.ts file at the folder level

endpoints.ts:

import { LoginRequest, LoginResponse, rtkQuery } from '~/services/rtk';

const authRtk = rtkQuery.injectEndpoints({
  endpoints: builder => ({
    login: builder.mutation<LoginResponse, LoginRequest>({
      query: data => ({
        url: '/token/',
        method: 'POST',
        data,
      }),
    }),
  }),
});

export { authRtk };
export const { useLoginMutation } = authRtk;

rtkQuery.ts:

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

import { cacher } from '~/services/rtk/utils';

export const rtkQuery = createApi({
  reducerPath: 'rtkReducer',
  baseQuery: fetchBaseQuery({ baseUrl: 'a' }),
  tagTypes: Object.values(cacher.tags),
  endpoints: () => ({}),
});

index.ts:

export { authRtk, useLoginMutation } from './modules/endpoints';
export { LoginRequest, LoginResponse } from './modules/types';
export { rtkQuery } from './rtkQuery';

And if I do that I get this error:

image

However, if I remove the authRtk import line from index.ts:

export { LoginRequest, LoginResponse } from './modules/types';
export { rtkQuery } from './rtkQuery';

Or I put the line export { rtkQuery } from './rtkQuery', work to

export { rtkQuery } from './rtkQuery';
export { LoginRequest, LoginResponse } from './modules/types';
export { rtkQuery } from './rtkQuery';

It's very stupid why the code does not work from the sequence of exports? If I put the line in the first place, eslint ordering (Run autofix to sort these exports!(simple-import-sort/exports)) starts to swear ..

I want to say that it works without problems on react-native!

markerikson commented 2 years ago

That looks to me like you've got a circular import problem, but I'd have to see the actual files to be sure.

VictorPulzz commented 2 years ago

@markerikson Hello! here my files

services/rtk/modules/endpoints.ts:

import { LoginRequest, LoginResponse, rtkQuery } from '~/services/rtk';

const authRtk = rtkQuery.injectEndpoints({
  endpoints: builder => ({
    login: builder.mutation<LoginResponse, LoginRequest>({
      query: data => ({
        url: '/token/',
        method: 'POST',
        data,
      }),
    }),
  }),
});

export { authRtk };
export const { useLoginMutation } = authRtk;

services/rtk/modules/types.ts:

export type LoginRequest = {
  email: string;
  password: string;
};
export type LoginResponse = {
  access: string;
  refresh: string;
};

services/rtk/rtkQuery.ts

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

import { cacher } from '~/services/rtk/utils';

export const rtkQuery = createApi({
  reducerPath: 'rtkReducer',
  baseQuery: fetchBaseQuery({ baseUrl: 'a' }),
  tagTypes: [],
  endpoints: () => ({}),
});

store/core/store.ts

import { configureStore } from '@reduxjs/toolkit';
import { FLUSH, PAUSE, PERSIST, persistStore, PURGE, REGISTER, REHYDRATE } from 'redux-persist';

import { rtkQuery } from '~/services/rtk/rtkQuery';
import { coreReducer } from '~/store/core/reducer';

const store = configureStore({
  reducer: coreReducer,
  middleware: getDefaultMiddleware =>
    getDefaultMiddleware({
      immutableCheck: { warnAfter: 128 },
      serializableCheck: {
        warnAfter: 128,
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }).concat(rtkQuery.middleware),
});

const persistor = persistStore(store);

export { persistor, store };

store/core/reducer.ts

import { AnyAction, combineReducers, Reducer } from '@reduxjs/toolkit';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

import { rtkQuery } from '~/services/rtk';
import { RootState } from '~/store/core/types';

import { userReducers } from '../modules/user/slice';

const userPersisted = persistReducer(
  {
    key: 'user',
    version: 1,
    storage,
    whitelist: ['auth'],
  },
  userReducers,
);

const combinedReducer = combineReducers({
  user: userPersisted,
  [rtkQuery.reducerPath]: rtkQuery.reducer,
});

const coreReducer: Reducer = (state: RootState, action: AnyAction) => {
  return combinedReducer(state, action);
};

export { combinedReducer, coreReducer };

store/core/slice.ts

import { createSlice, PayloadAction } from '@reduxjs/toolkit';

import { authRtk } from '~/services/rtk/modules/endpoints';
import { LoginResponse } from '~/services/rtk/modules/types';
import { UserState } from '~/store/modules/user/types';

const initialState: UserState = {
  auth: null,
};

const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    setAuth: (state, { payload }) => {
      state.auth = payload;
    },
    signOut: state => {
      state.auth = null;
    },
  },
  extraReducers: builder => {
    builder.addMatcher(
      authRtk.endpoints.login.matchFulfilled,
      (state: UserState, action: PayloadAction<LoginResponse>) => {
        userSlice.caseReducers.setAuth(state, action);
      },
    );
  },
});

export const userReducers = userSlice.reducer;
export const { setAuth, signOut } = userSlice.actions;

Structure:

image
VictorPulzz commented 2 years ago

Also I try to setup clear project and got this error: Cannot access 'rtkQuery' before initialization Wtf?? This link https://github.com/VictorPulzz/test When I use

extraReducers: builder => {
        builder.addMatcher(
            authRtk.endpoints.login.matchFulfilled,
            (state: UserState, action: PayloadAction<LoginResponse>) => {
                userSlice.caseReducers.setAuth(state, action);
            },
        );
    },

I got this error...

markerikson commented 2 years ago

Yeah, I'm about 99% sure you've got a circular import problem. I can't trace the path all the way right now, but you're going back and forth between the store, the reducers, and the slice files, and that looks like it's resulting in a cycle. You'll need to rearrange those files and imports somehow to avoid this.

Btw, as a side note, please don't do this:

const coreReducer: Reducer = (state: RootState, action: AnyAction) => {
    return combinedReducer(state, action);
};

Not only is it useless and unnecessary, you're also throwing away the types. The combinedReducer is your root reducer, and you don't need to wrap it in another.

VictorPulzz commented 2 years ago

@markerikson Okay I will check this, about this code, I just cut include, in coreReducer I handle logout user, yep I need change this, thanks!