I'm using redux toolkit.
In postSlice.js, has a post state.
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
const fetchPosts = createAsyncThunk("Posts/fetchPosts", ({ search }) => {
if (search){
// fetch post with search option
return response.data;
}
// fetch posts
return response.data;
});
const postSlice = createSlice({
name: "Posts",
initialState: {
posts: [],
},
extraReducers: {
[fetchPosts.fulfilled]: (state, action) => {
return { ...state, posts: action.payload.posts };
}
},
});
export default postSlice.reducer;
In index page we'll represent posts
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { fetchPosts } from "postSlice";
function Index() {
const dispatch = useDispatch();
const search = // get from url query string
const posts = useSelector(state=>state.posts.posts);
useEffect(() => {
dispatch(fetchPosts({ search }));
}, [dispatch, search]);
return( <div>
// show posts
</div>);
}
export default Index;
On first page load, posts will be added to state in redux. And then when I enter a search request, the first fetched posts are still exist, while searched posts are fetching in database.
Actually I want to show a loading spinner while post are fecthing(when I make a search request). To show that I check with posts.length in index page.
I tried with this way.
Add a resetPosts reducer in postSlice.js
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
const fetchPosts = createAsyncThunk("Posts/fetchPosts", ({ search }) => {
if (search){
// fetch post with search option
return response.data;
}
// fetch posts
return response.data;
});
const postSlice = createSlice({
name: "Posts",
initialState: {
posts: [],
},
reducer: {
resetPosts: ( state, action ) => {
return { ...state, posts: [] };
}
},
extraReducers: {
[fetchPosts.fulfilled]: (state, action) => {
return { ...state, posts: action.payload.posts };
}
},
});
export const { resetPosts } = postSlice.actions;
export default postSlice.reducer;
And use resetPosts before dispatch of fetchPosts
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { resetPosts, fetchPosts } from "postSlice";
function Index() {
const dispatch = useDispatch();
const search = // get from url query string
const posts = useSelector(state=>state.posts.posts);
useEffect(() => {
dispatch(fetchPosts({ search }));
}, [dispatch, search]);
return( <div>
// show posts
</div>);
}
export default Index;
But this solution is not working.
CodePudding user response:
You can use the thunk action for pending: https://redux-toolkit.js.org/api/createAsyncThunk#type
Like so:
const postSlice = createSlice({
name: "Posts",
initialState: {
posts: [],
},
extraReducers: {
[fetchPosts.fulfilled]: (state, action) => {
return { ...state, posts: action.payload.posts };
},
[fetchPosts.pending]: (state, action) => {
state.posts = [];
}
},
});
P.S. You do not need to pass a new object in the reducer. RTK uses Immer, which allows you to mutate the object in the reducer methods that you write, and then Immer is making the diff and sending the new variable.
CodePudding user response:
like Mihail Vratchanski said, you need a "fetchPosts.pending" reducer. Then, you have two options to know that the call is still pending.
- (Option A) The initial value of the "posts" should be null, and set to null in this reduces. So you know that if the variable is null, you don't have an answer yet. (take in mind that you could get an empty list from a server response).
- (Option B) In this reducer, you can set a loading variable to true. (in the "fetchPosts.fulfilled" reducer set loading variable to false).
Regards Zaquiel