Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Comments on Redux Toolkit Issue with Managing States
Post
Redux Toolkit Issue with Managing States
I am all new with using Redux Toolkit, I followed the official documentation to setup the store:
// store.ts
import { configureStore, ThunkAction, Action, combineReducers } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import authReducer from '../features/authentication/redux/authSlice';
import delegationReducer from '../features/delegations/redux/delegationSlice'
export const store = configureStore({
reducer: {
counter: counterReducer,
auth: authReducer,
delegations: delegationReducer
},
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
As you can see, I have three reducers: authReducer, delegationReducer and counterReducer which are not related to each other. The issue is when I try to dispatch an action to change the auth state (for example getAuthenticatedUser()) all the other reducers partially reset to initial state. The same goes for the other reducers.
I will provide you the code of authSlice.tsx and delegateSlice.tsx to demonstrate better the problem (I apologize if it is too long):
// authSlice.tsx
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios, { AxiosInstance } from "axios";
import { toast } from "react-toastify";
const authInstance: AxiosInstance = axios.create({
baseURL: "http://localhost:4000/api/auth",
});
interface AuthState {
token: any;
isLoading: boolean | null;
isAuthenticated: boolean | null;
userData: any;
error: any;
}
export const registerAdmin = createAsyncThunk(
"auth/register",
async (userData: any, { rejectWithValue }) => {
try {
const response = await authInstance.post("/register", userData, {
headers: {
"Content-Type": "application/json",
},
});
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const loginUser = createAsyncThunk(
"auth/login",
async (userData: any, { rejectWithValue }) => {
try {
const response = await authInstance.post("/", userData, {
headers: {
"Content-Type": "application/json",
},
});
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const getAuthenticatedUser = createAsyncThunk(
"auth/get_authenticated_user",
async (_, { rejectWithValue }) => {
try {
const response = await authInstance.get("/", {
headers: {
Authorize: localStorage.getItem("auth_token"),
},
});
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const requestPasswordChange = createAsyncThunk(
"auth/request_password_change",
async (userData: any, { rejectWithValue }) => {
try {
const response = await authInstance.post("/password", userData, {
headers: {
"Content-Type": "application/json",
},
});
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
const initialState = {
token: localStorage.getItem("auth_token"),
isLoading: false,
isAuthenticated: localStorage.getItem("auth_token") !== null,
error: null,
userData: null,
} as AuthState;
const authPending = (state: AuthState) => {
state.isLoading = true;
state.error = null;
state.userData = null;
state.isAuthenticated = false;
};
const authRejected = (state: AuthState, action: any) => {
localStorage.removeItem("auth_token");
state.isLoading = false;
state.error = action.payload;
state.isAuthenticated = false;
};
export const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
logoutUser(state: AuthState) {
localStorage.removeItem("auth_token");
state.isLoading = false;
state.error = null;
state.isAuthenticated = false;
state.userData = null;
}
},
extraReducers: (builder) => {
builder
.addCase(registerAdmin.pending, authPending)
.addCase(loginUser.pending, authPending)
.addCase(getAuthenticatedUser.pending, authPending)
.addCase(requestPasswordChange.pending, authPending)
.addCase(registerAdmin.rejected, authRejected)
.addCase(loginUser.rejected, authRejected)
.addCase(getAuthenticatedUser.rejected, authRejected)
.addCase(requestPasswordChange.rejected, authRejected)
.addCase(registerAdmin.fulfilled, (state: AuthState, { payload }) => {
state.isLoading = false;
state.isAuthenticated = true;
state.userData = null;
state.error = null;
localStorage.setItem("auth_token", payload.token);
})
.addCase(loginUser.fulfilled, (state: AuthState, { payload }) => {
state.isLoading = false;
state.isAuthenticated = true;
state.userData = null;
state.error = null;
localStorage.setItem("auth_token", payload.token);
})
.addCase(
getAuthenticatedUser.fulfilled,
(state: AuthState, { payload }) => {
state.isLoading = false;
state.isAuthenticated = true;
state.userData = payload;
state.error = null;
state.token = localStorage.getItem("auth_token");
}
)
.addCase(
requestPasswordChange.fulfilled,
(state: AuthState, { payload }) => {
state.isLoading = false;
state.isAuthenticated = false;
state.userData = false;
state.error = null;
state.token = localStorage.getItem("auth_token");
}
)
.addDefaultCase((state: AuthState) => {
state.token = localStorage.getItem("auth_token");
state.isLoading = false;
state.isAuthenticated = localStorage.getItem("auth_token") ? true : false
state.error = null;
state.userData = null;
});
},
});
export const { logoutUser } = authSlice.actions;
export default authSlice.reducer;
// delegationSlice.tsx
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios, { AxiosInstance } from "axios";
import { toast } from "react-toastify";
const delegationsInstance: AxiosInstance = axios.create({
baseURL: "http://localhost:4000/api/delegations",
});
interface DelegationsState {
delegationList: any;
delegateList: any;
delegationData: any;
delegateData: any;
isLoading: boolean | null;
error: any;
storedDelegationData: any;
}
export const addDelegation = createAsyncThunk(
"delegations/add_delegation",
async (delegationData: any, { rejectWithValue }) => {
try {
const response = await delegationsInstance.post("/", delegationData, {
headers: {
"Content-Type": "application/json",
Authorize: localStorage.getItem("auth_token"),
},
});
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const addDelegate = createAsyncThunk(
"delegations/add_delegate",
async (data: any, { rejectWithValue }) => {
const { delegationId, delegateData } = data;
try {
const response = await delegationsInstance.post(
`/delegates/${delegationId}`,
delegateData,
{
headers: {
"Content-Type": "application/json",
Authorize: localStorage.getItem("auth_token"),
},
}
);
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const getDelegationList = createAsyncThunk(
"delegations/get_delegation_list",
async (_, { rejectWithValue }) => {
try {
const response = await delegationsInstance.get("/delegations", {
headers: {
Authorize: localStorage.getItem("auth_token"),
},
});
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const getDelegation = createAsyncThunk(
"delegations/get_delegation",
async (data: any, { rejectWithValue }) => {
const { id } = data;
try {
const response = await delegationsInstance.get(`/delegations/${id}`, {
headers: {
Authorize: localStorage.getItem("auth_token"),
},
});
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const getDelegateList = createAsyncThunk(
"delegations/get_delegate_list",
async (_, { rejectWithValue }) => {
try {
const response = await delegationsInstance.get("/delegates", {
headers: {
Authorize: localStorage.getItem("auth_token"),
},
});
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const getDelegate = createAsyncThunk(
"delegations/get_delegate",
async (data: any, { rejectWithValue }) => {
const { id } = data;
try {
const response = await delegationsInstance.get(`/delegates/${id}`, {
headers: {
Authorize: localStorage.getItem("auth_token"),
},
});
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const updateDelegation = createAsyncThunk(
"delegations/update_delegation",
async (data: any, { rejectWithValue }) => {
const { id, delegationData } = data;
try {
const response = await delegationsInstance.put(
`/delegations/${id}`,
delegationData,
{
headers: {
"Content-Type": "application/json",
Authorize: localStorage.getItem("auth_token"),
},
}
);
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const updateDelegate = createAsyncThunk(
"delegations/update_delegate",
async (data: any, { rejectWithValue }) => {
const { id, delegateData } = data;
try {
const response = await delegationsInstance.put(
`/delegates/${id}`,
delegateData,
{
headers: {
"Content-Type": "application/json",
Authorize: localStorage.getItem("auth_token"),
},
}
);
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const removeDelegation = createAsyncThunk(
"delegations/remove_delegation",
async (data: any, { rejectWithValue }) => {
const { id } = data;
try {
const response = await delegationsInstance.put(
`/delegations/remove/${id}`,
{
headers: {
Authorize: localStorage.getItem("auth_token"),
},
}
);
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
export const removeDelegate = createAsyncThunk(
"delegations/remove_delegate",
async (data: any, { rejectWithValue }) => {
const { id } = data;
try {
const response = await delegationsInstance.put(
`/delegates/remove/${id}`,
{
headers: {
Authorize: localStorage.getItem("auth_token"),
},
}
);
return response.data;
} catch (error: any) {
const errorMessages = error?.response?.data?.errors?.map(
(err: any) => err.msg
);
toast.error(errorMessages?.join("\n"));
return rejectWithValue(error.response.data);
}
}
);
const initialState = {
delegationList: [],
delegateList: [],
delegationData: null,
delegateData: null,
isLoading: false,
error: null,
storedDelegationData: null,
} as DelegationsState;
const delegPending = (state: DelegationsState) => {
state.isLoading = true;
state.error = null;
};
const delegRejected = (state: DelegationsState, action: any) => {
state.isLoading = false;
state.error = action.payload;
};
export const delegationSlice = createSlice({
name: "delegations",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(addDelegation.pending, delegPending)
.addCase(addDelegate.pending, delegPending)
.addCase(getDelegationList.pending, delegPending)
.addCase(getDelegateList.pending, delegPending)
.addCase(getDelegation.pending, delegPending)
.addCase(getDelegate.pending, delegPending)
.addCase(updateDelegation.pending, delegPending)
.addCase(updateDelegate.pending, delegPending)
.addCase(removeDelegation.pending, delegPending)
.addCase(removeDelegate.pending, delegPending)
.addCase(addDelegation.rejected, delegRejected)
.addCase(addDelegate.rejected, delegRejected)
.addCase(getDelegationList.rejected, delegRejected)
.addCase(getDelegateList.rejected, delegRejected)
.addCase(getDelegation.rejected, delegRejected)
.addCase(getDelegate.rejected, delegRejected)
.addCase(updateDelegation.rejected, delegRejected)
.addCase(updateDelegate.rejected, delegRejected)
.addCase(removeDelegation.rejected, delegRejected)
.addCase(removeDelegate.rejected, delegRejected)
.addCase(
addDelegation.fulfilled,
(state: DelegationsState, { payload }) => {
state.isLoading = false;
state.delegationList = [...state.delegationList, payload];
state.delegateList = [];
state.delegationData = null;
state.delegateData = null;
state.error = null;
state.storedDelegationData = null;
}
)
.addCase(
addDelegate.fulfilled,
(state: DelegationsState, { payload }) => {
state.isLoading = false;
state.delegationList = [];
state.delegateList = [...state.delegateList, payload];
state.delegationData = null;
state.delegateData = null;
state.error = null;
state.storedDelegationData = null;
}
)
.addCase(
getDelegationList.fulfilled,
(state: DelegationsState, { payload }) => {
state.isLoading = false;
state.delegationList = payload;
}
)
.addCase(
getDelegateList.fulfilled,
(state: DelegationsState, { payload }) => {
state.isLoading = false;
state.delegationList = [];
state.delegateList = payload;
state.delegationData = null;
state.delegateData = null;
state.error = null;
state.storedDelegationData = null;
}
)
.addCase(
getDelegation.fulfilled,
(state: DelegationsState, { payload }) => {
state.isLoading = false;
state.delegationList = [];
state.delegateList = [];
state.delegationData = payload;
state.delegateData = null;
state.error = null;
state.storedDelegationData = null;
}
)
.addCase(
getDelegate.fulfilled,
(state: DelegationsState, { payload }) => {
state.isLoading = false;
state.delegationList = [];
state.delegateList = [];
state.delegationData = null;
state.delegateData = payload;
state.error = null;
state.storedDelegationData = null;
}
)
.addCase(
updateDelegation.fulfilled,
(state: DelegationsState, { payload }) => {
state.isLoading = false;
state.delegationList = state.delegationList.map((el: any) =>
el._id === payload._id ? payload : el
);
state.delegateList = [];
state.delegationData = null;
state.delegateData = null;
state.error = null;
state.storedDelegationData = null;
}
)
.addCase(
removeDelegation.fulfilled,
(state: DelegationsState, { payload }) => {
state.isLoading = false;
state.delegationList = state.delegationList.map((el: any) =>
el._id === payload._id ? payload : el
);
state.delegateList = [];
state.delegationData = null;
state.delegateData = null;
state.error = null;
state.storedDelegationData = null;
}
)
.addCase(
updateDelegate.fulfilled,
(state: DelegationsState, { payload }) => {
state.isLoading = false;
state.delegationList = [];
state.delegateList = state.delegateList.map((el: any) =>
el._id === payload._id ? payload : el
);
state.delegationData = null;
state.delegateData = null;
state.error = null;
state.storedDelegationData = null;
}
)
.addCase(
removeDelegate.fulfilled,
(state: DelegationsState, { payload }) => {
state.isLoading = false;
state.delegationList = [];
state.delegateList = state.delegateList.map((el: any) =>
el._id === payload._id ? payload : el
);
state.delegationData = null;
state.delegateData = null;
state.error = null;
state.storedDelegationData = null;
}
)
.addDefaultCase((state: DelegationsState) => {
state.isLoading = false;
state.delegationList = [];
state.delegateList = [];
state.delegationData = null;
state.delegateData = null;
state.error = null;
state.storedDelegationData = null;
});
},
});
export const {} = delegationSlice.actions;
export default delegationSlice.reducer;
As you can see, the two slices are independent of each other, when I try to dispatch getDelegationList(), I get successfully the list of delegations but userData becomes null (isAuthenticated still true and there is a token in the local storage). when I try to dispatch getAuthenticatedUser(), I get successfully the user data but the delegation list becomes []
.
What causes this problem and how to fix it ?
2 comment threads