I am building a simple application. I am using redux toolkit for state management
It has to query the database to check if user is authenticated.
if yes it sets name,email etc for the user
while it is checking the database I want a loading spinner on the frontend the spinner takes the entire screen and will be shown anytime there a database query. for that reason I want to have a seperate app state that has two values viz loading and error
appSlice
const initialState = {loading : false,error:false,error_msg:null}
export const appSlice = createSlice({
name : 'app',
initialState : {value : initialState},
reducers : {
toggle : (state,action)=>{
state.value.loading = action.payload
}
},
});
userSlice
const initialState = { name: "", email: "", role: "", isAuthenticated: false };
export const authenticateUser = createAsyncThunk(
"user/authenticate",
async () => {
let res = await axiosInstance.get("/user/authenticate");
return res.data;
}
);
export const userSlice = createSlice({
name: "user",
initialState: { value: initialState },
reducers: {},
extraReducers: (builder) => {
builder.addCase(authenticateUser.pending, (state, action) => {
//change loading to true for app slice something like (state.app.loading = true)
});
builder.addCase(authenticateUser.fulfilled, (state, action) => {
state.value = action.payload
});
builder.addCase(authenticateUser.rejected, (state, action) => {
//change error to true for app slice something like (state.app.error = true)
//change error_msg to action.payload for app slice
//something like (state.app.error_msg = action.payload)
});
},
});
You can handle one action in different state slices. You should reference the authenticateUser.pending
and authenticateUser.rejected
action creators from the userSlice
file in appSlice
, and handle them in extraReducers
of appSlice
.
For example, if the user/authenticate
action fails with an error.
import { configureStore, createAsyncThunk, createSlice } from '@reduxjs/toolkit';
export const authenticateUser = createAsyncThunk<any, any, { rejectValue: { error: string } }>(
'user/authenticate',
async (_, { rejectWithValue }) => {
// success
// return { name: 'teresa teng', email: 'teresa@gmail.com', role: 'admin', isAuthenticated: true };
// fail
return rejectWithValue({ error: 'network error' }) as unknown as { error: string };
},
);
export const userSlice = createSlice({
name: 'user',
initialState: { value: { name: '', email: '', role: '', isAuthenticated: false } },
reducers: {},
extraReducers: (builder) => {
builder.addCase(authenticateUser.fulfilled, (state, action) => {
state.value = action.payload;
});
},
});
export const appSlice = createSlice({
name: 'app',
initialState: { value: { loading: false, error: false, error_msg: '' } },
reducers: {
toggle: (state, action) => {
state.value.loading = action.payload;
},
},
extraReducers: (builder) => {
builder.addCase(authenticateUser.pending, (state, action) => {
state.value.loading = true;
});
builder.addCase(authenticateUser.rejected, (state, action) => {
state.value.error = true;
state.value.error_msg = action.payload?.error || 'unknown error';
});
},
});
const store = configureStore({
reducer: { app: appSlice.reducer, user: userSlice.reducer },
});
store.dispatch(authenticateUser({}));
store.subscribe(() => {
console.log(store.getState());
});
Output:
{
app: { value: { loading: true, error: true, error_msg: 'network error' } },
user: { value: { name: '', email: '', role: '', isAuthenticated: false } }
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.