I encounter an error which I had hard times to isolate the source.
the web app is built in ReactJs / Vite and RTK.
I created a simple RTK slice.
When I add an asyncThunk to my slice the app crash and I get the following error:
Uncaught ReferenceError: Cannot access 'tagsSlice' before initialization
I've done that one thousand times.
Adding a regular action reducers is fine.
I'm not able to understand why.
The only thing new compared to my previous project is Vite.
The error is thrown from the store.ts, here's the file :
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import thunk from 'redux-thunk';
import appTimeSpanSlice from './slices/appTimeSpanSlice';
import authenticationSlice from './slices/authenticationSlice';
import uiBehaviorHandlerSlice from './slices/uiBehaviorHandlerSlice';
import intervalMiddleware from './middlewares/intervalMiddleware';
import pollingMiddleware from './middlewares/pollingMiddleware';
import tagsSlice from './slices/tagsSlice'
export const store = configureStore({
reducer: {
tag: tagsSlice, // <--- ERROR IS THROWN HERE
appTime: appTimeSpanSlice,
authentication: authenticationSlice,
ui: uiBehaviorHandlerSlice,
},
middleware: [thunk, intervalMiddleware, pollingMiddleware],
});
export type RootState = ReturnType<typeof store.getState>
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
Here's my slice :
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { tagApi } from 'services/tag/tagApi'
export const createTag = createAsyncThunk(
'tags/createTags',
async (body, thunkAPI) => {
const response = await tagApi.createTag(body);
return response.data;
}
);
enum Status {
IDLE = 'idle',
PENDING = "pending",
FULFILLED = "fulfilled",
REJECTED = "rejected"
}
interface TagsState {
tags: [];
fetchTagsStatus: Status;
createTagStatus: Status;
}
const initialState = {
tags: [],
fetchTagsStatus: Status.IDLE,
createTagStatus: Status.IDLE
} as TagsState
const tagsSlice = createSlice({
name: 'tags',
initialState,
reducers: {
testAction: () => { console.log("Hello")}
},
extraReducers: (builder) => {
builder
.addCase(createTag.pending, state => {
state.createTagStatus = Status.PENDING;
})
.addCase(createTag.fulfilled, (state, { payload }) => {
state.createTagStatus = Status.FULFILLED;
})
.addCase(createTag.rejected, (state, { error }) => {
state.createTagStatus = Status.REJECTED;
})
},
})
const { actions, reducer } = tagsSlice;
export const { testAction } = actions;
export default reducer;
To sum up, in order to reproduce the issue, 2 conditions should be met:
TROUBLESHOOTING STEPS:
testAction
action is imported: no errortestAction
action is imported: errorWhat would potentially cause this behavior ?
The issue was that I was trying to access the store outside a React component.
Here's what I had:
import api from 'services/api';
import { selectStart, selectEnd } from 'store/slices/appTimeSpanSlice';
import { store } from 'store/store';
export const tagApi = {
fetchAllTags: async () => {
const start = selectStart(store.getState()); <<< not allowed
const end = selectEnd(store.getState()); <<< not allowed
const url = `/api/tags?start=${start}&end=${end}`;
return await api.get(url);
},
createTag: async (body: any) => {
const url = `/api/tags`;
return await api.post(url, body);
}
};
Here's how I fixed it:
import api from 'services/api';
import { selectStart, selectEnd } from 'store/slices/appTimeSpanSlice';
export const tagApi = {
fetchAllTags: async (start, end) => {
const url = `/api/tags?start=${start}&end=${end}`;
return await api.get(url);
},
createTag: async (body: any) => {
const url = `/api/tags`;
return await api.post(url, body);
}
};