I have this slice:
const studentSlice = createSlice({
name: 'studentSlice',
initialState: {
data: {},
loading: false,
error: null,
},
reducers: {
setData: (state, action) => {
state.data = action.payload;
},
setLoading: (state, action) => {
state.loading = action.payload;
},
setError: (state, action) => {
state.error = action.payload;
},
},
});
export const { setData, setLoading, setError } = studentSlice.actions;
export default studentSlice.reducer;
I have tried to create an action that uses multiple reducer:
export const getStudentData =
(): any => async (dispatch: any) => {
try {
console.log("here");
dispatch(setLoading(true));
const {
data,
isLoading,
error
} = await personApi.endpoints.getStudentList.useQueryState(); // Using RTK Query
console.log(data);
if (isLoading) {
return;
}
if (error) {
throw error;
}
dispatch(setData(data));
dispatch(setLoading(false));
} catch (error) {
dispatch(setError(error));
}
};
In my Components, using useEffect
, I am trying to dispatch the action
const StudentProfile = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getStudentData())
}, [])
const students = useSelector((state: any) => state.student.data))
return ... // some components that use this students
}
But the getStudentData
action doesn't execute (no log printed). Why this happened and how to dispatch the action properly?
I have recently tried using extraReducers here but the state of the slice doesn't change either.
Here is my API slice code:
const personApi = api.injectEndpoints({
endpoints: (builder) => ({
getStudentList: builder.query<StudentsResult, void>({
query: () => ({
url: 'students',
method: 'POST',
body: JSON.stringify({
// some fields
}),
}),
providesTags: ['Students'],
}),
}),
});
I want to store the data from
createApi
to the [studentSlice
] slice.
You can create reducer cases to match the pending, fulfilled, and rejected statuses of active queries/mutations.
const studentSlice = createSlice({
name: 'studentSlice',
initialState: {
data: {},
loading: false,
error: null,
},
reducers: {
....
},
extraReducers: builder => {
builder
.addMatcher(
personApi.endpoints.getStudentList.matchPending,
(state) => {
state.loading = true;
},
)
.addMatcher(
personApi.endpoints.getStudentList.matchFulfilled,
(state, action) => {
state.loading = false;
state.error = null;
state.data = action.payload;
},
)
.addMatcher(
personApi.endpoints.getStudentList.matchRejected,
(state, action) => {
state.loading = false;
state.error = action.payload;
},
);
},
});
Initiate the getStudentList
query using either the generated useGetStudentList
or useLazyGetStudentList
hooks.
const queryResult = useGetStudentList();
const [getStudents, queryResult] = useLazyGetStudentList();
Alternatively, if you really still wanted to use a Thunk, then use createAsyncThunk
so the pending, fulfilled, and rejected actions are generated for you, and initiate the endpoint manually. Note that since you still need the extraReducers
that there's no net gain over the preferred method above using the query matchers.
Example:
import { createAsyncThunk } from '@reduxjs/toolkit';
export const getStudentData = createAsyncThunk(
"students/getStudentData",
async (_, thunkApi) => {
try {
const { data, error } = await thunkApi.dispatch(
personApi.endpoints.getStudentList.initiate()
);
if (error) {
throw error;
}
return data;
} catch (error) {
return thunkApi.rejectWithValue(error);
}
}
);
const studentSlice = createSlice({
name: 'studentSlice',
initialState: {
data: {},
loading: false,
error: null,
},
reducers: {
....
},
extraReducers: builder => {
builder
.addCase(getStudentData.pending, (state) => {
state.loading = true;
})
.addCase(getStudentData.fulfilled, (state, action) => {
state.loading = false;
state.error = null;
state.data = action.payload;
})
.addCase(getStudentData.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
});
},
});