Reputation: 463
I'm currently struggling to solve this error which i believe might be a circular reference dependency issue within my react redux application. It was working fine at first but then all of a sudden my app is throwing this error:
From the error, it says checkAuth cannot be accessed before initialisation. checkAuth is a createAsyncThunk that is exported from authentication slice and is imported into the application slice to be added as extra reducers. Right now I am very confused on how to solve this issue. Hopefully you guys could help me out.
Here are the codes: store.js
import { configureStore } from "@reduxjs/toolkit";
import authReducer from "./authentication/authenticationSlice";
import appReducer from "./application/appSlice";
import errorReducer from "./error/errorSlice";
const store = configureStore({
reducer: {
auth: authReducer,
error: errorReducer,
app: appReducer,
},
});
export default store;
authenticationSlice.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { axiosPublic, axiosPrivate } from "../../utils/axios";
const initialState = {
isAuthLoading: false,
authUser: null,
};
const checkAuth = createAsyncThunk("auth/checkAuth", async (_, thunkAPI) => {
try {
const response = await axiosPrivate.get("/auth");
return response.data;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
});
const login = createAsyncThunk("auth/login", async (credentials, thunkAPI) => {
try {
const response = await axiosPublic.post("/auth/login", credentials, {
withCredentials: true,
});
return response.data;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
});
const logout = createAsyncThunk("auth/logout", async (_, thunkAPI) => {
try {
await axiosPrivate.post("/auth/logout", null, { withCredentials: true });
return;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
});
const reqOTPHandler = createAsyncThunk(
"auth/reqOTPHandler",
async ({ endpoint, credentials }, thunkAPI) => {
try {
await axiosPublic.post(endpoint, credentials);
return;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
}
);
const signup = createAsyncThunk(
"auth/submitOTP",
async (credentials, thunkAPI) => {
try {
const response = await axiosPublic.post(
"/auth/confirm-signup",
credentials,
{
withCredentials: true,
}
);
return response.data;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
}
);
const reqResetHandler = createAsyncThunk(
"/auth/reqResetHandler",
async ({ endpoint, token }, thunkAPI) => {
try {
await axiosPublic.post(endpoint, {
token,
});
return;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
}
);
const resetPassword = createAsyncThunk(
"auth/resetPassword",
async ({ endpoint, credentials }, thunkAPI) => {
try {
await axiosPublic.post(endpoint, credentials);
return;
} catch (e) {
return thunkAPI.rejectWithValue(e.response?.data || e);
}
}
);
const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
loading: (state) => {
state.isAuthLoading = true;
},
idle: (state) => {
state.isAuthLoading = false;
},
updateAuthUser: (state, action) => {
state.authUser = action.payload;
},
removeAuthUser: (state) => {
state.authUser = null;
},
},
extraReducers: {
[checkAuth.fulfilled]: (state, action) => {
state.authUser = action.payload;
},
[login.pending]: (state) => {
state.isAuthLoading = true;
},
[login.fulfilled]: (state, action) => {
state.isAuthLoading = false;
state.authUser = action.payload;
},
[login.rejected]: (state, action) => {
state.isAuthLoading = false;
},
[logout.fulfilled]: (state, action) => {
state.authUser = null;
},
[reqOTPHandler.pending]: (state) => {
state.isAuthLoading = true;
},
[reqOTPHandler.fulfilled]: (state) => {
state.isAuthLoading = false;
},
[reqOTPHandler.rejected]: (state) => {
state.isAuthLoading = false;
},
[signup.pending]: (state) => {
state.isAuthLoading = true;
},
[signup.fulfilled]: (state, action) => {
state.isAuthLoading = false;
state.authUser = action.payload;
},
[signup.rejected]: (state) => {
state.isAuthLoading = false;
},
[reqResetHandler.pending]: (state) => {
state.isAuthLoading = true;
},
[reqResetHandler.fulfilled]: (state, action) => {
state.isAuthLoading = false;
},
[reqResetHandler.rejected]: (state) => {
state.isAuthLoading = false;
},
[resetPassword.pending]: (state) => {
state.isAuthLoading = true;
},
[resetPassword.fulfilled]: (state, action) => {
state.isAuthLoading = false;
},
[resetPassword.rejected]: (state) => {
state.isAuthLoading = false;
},
},
});
const { reducer } = authSlice;
const { loading, idle, updateAuthUser, removeAuthUser } = authSlice.actions;
export {
loading as authLoading,
idle as authIdle,
updateAuthUser,
removeAuthUser,
// thunks
checkAuth,
login,
logout,
reqOTPHandler,
signup,
reqResetHandler,
resetPassword,
};
export default reducer;
appSlice.js
import { createSlice } from "@reduxjs/toolkit";
import { checkAuth, logout } from "../authentication/authenticationSlice";
const initialState = {
// Global App loading state
isInitializing: true,
isAppLoading: false,
currentBranch: null,
currentRole: null,
};
const appSlice = createSlice({
name: "app",
initialState,
reducers: {
appInitializing: (state) => {
state.isInitializing = true;
},
appInitialized: (state) => {
state.isInitializing = false;
},
appLoading: (state) => {
state.isAppLoading = true;
},
appFullfilled: (state) => {
state.isAppLoading = false;
},
},
extraReducers: {
// auth
[checkAuth.pending]: (state) => {
state.isInitializing = true;
},
[checkAuth.fulfilled]: (state) => {
state.isInitializing = false;
},
[checkAuth.rejected]: (state) => {
state.isInitializing = false;
},
[logout.pending]: (state) => {
state.isAppLoading = true;
},
[logout.fulfilled]: (state) => {
state.isAppLoading = false;
},
[logout.rejected]: (state) => {
state.isAppLoading = false;
},
},
});
const { reducer } = appSlice;
export const { appInitializing, appInitialized, appLoading, appFullfilled } =
appSlice.actions;
export default reducer;
Upvotes: 0
Views: 3253
Reputation: 44078
Don't use the extraReducers
object notation - that one will be deprecated soon as it doesn't work with TypeScript - and has this exact problem. If you use the builder notation instead, that will work.
extraReducers: builder => {
// auth
builder.addCase(checkAuth.pending. (state) => {
state.isInitializing = true;
});
...
}
Upvotes: 3