I am trying to make a simple blog webpage with react and redux(going along a tutorial). I created a redux store, made a posts slice that is used to manage everything that has to do with the posts and i am having some issues with fetching the data using redux thunk. When i run the blog page i see two of every post. Not sure if the issue is from the component i used to dispatch the fetchPosts() function and display the posts or some error i made in the slice itself. Below is my code and a jsFiddle with my project. I'd really appreciate it if someone can help me check where i went wrong.
PostsList.js
const PostsList = () => {
const dispatch = useDispatch();
const posts = useSelector(selectAllPosts);
const postStatus = useSelector(getPostsStatus);
const error = useSelector(getPostsError);
useEffect(() => {
if (postStatus === "idle") {
dispatch(fetchPosts());
}
}, [postStatus, dispatch]);
let content;
if (postStatus === "loading") {
content = <p>"Loading..."</p>;
} else if (postStatus === "succeeded") {
const orderedPosts = posts
.slice()
.sort((a, b) => b.date.localeCompare(a.date));
content = orderedPosts.map((post) => (
<PostsExcerpt key={post.id} post={post} />
));
} else if (postStatus === "failed") {
content = <p>{error}</p>;
}
return (
<section>
<h2>Posts</h2>
{content}
</section>
);
};
export default PostsList;
PostsSlice.js
import { createSlice, nanoid, createAsyncThunk } from "@reduxjs/toolkit";
import { sub } from "date-fns";
import axios from "axios";
const POSTS_URL = "https://jsonplaceholder.typicode.com/posts";
const initialState = {
posts: [],
status: "idle",
error: null,
};
//Fetch Posts Function
export const fetchPosts = createAsyncThunk("posts/fetchPosts", async () => {
const response = await axios.get(POSTS_URL);
return response.data;
});
export const addNewPost = createAsyncThunk(
"posts/addNewPost",
async (initialPost) => {
const response = await axios.post(POSTS_URL, initialPost);
return response.data;
}
);
const postsSlice = createSlice({
name: "posts",
initialState,
reducers: {
postAdded: {
reducer(state, action) {
state.posts.push(action.payload);
},
prepare(title, content, userId) {
return {
payload: {
id: nanoid(),
title,
content,
date: new Date().toISOString(),
userId,
reactions: {
thumbsUp: 0,
wow: 0,
heart: 0,
rocket: 0,
coffee: 0,
},
},
};
},
},
reactionAdded(state, action) {
const { postId, reaction } = action.payload;
const existingPost = state.posts.find((post) => post.id === postId);
if (existingPost) {
existingPost.reactions[reaction]++;
}
},
},
extraReducers(builder) {
builder
.addCase(fetchPosts.pending, (state) => {
state.status = "loading";
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.status = "succeeded";
// Adding date and reactions
let min = 1;
const loadedPosts = action.payload.map((post) => {
post.date = sub(new Date(), { minutes: min++ }).toISOString();
post.reactions = {
thumbsUp: 0,
wow: 0,
heart: 0,
rocket: 0,
coffee: 0,
};
return post;
});
// Add any fetched posts to the array
state.posts = state.posts.concat(loadedPosts);
})
.addCase(fetchPosts.rejected, (state, action) => {
state.status = "failed";
state.error = action.error.message;
})
.addCase(addNewPost.fulfilled, (state, action) => {
// Fix for API post IDs:
// Creating sortedPosts & assigning the id
// would be not be needed if the fake API
// returned accurate new post IDs
const sortedPosts = state.posts.sort((a, b) => {
if (a.id > b.id) return 1;
if (a.id < b.id) return -1;
return 0;
});
action.payload.id = sortedPosts[sortedPosts.length - 1].id + 1;
// End fix for fake API post IDs
action.payload.userId = Number(action.payload.userId);
action.payload.date = new Date().toISOString();
action.payload.reactions = {
thumbsUp: 0,
hooray: 0,
heart: 0,
rocket: 0,
eyes: 0,
};
console.log(action.payload);
state.posts.push(action.payload);
});
},
});
export const selectAllPosts = (state) => state.posts.posts;
export const getPostsStatus = (state) => state.posts.status;
export const getPostsError = (state) => state.posts.error;
export const { postAdded, reactionAdded } = postsSlice.actions;
export default postsSlice.reducer;
Incase nothing i suspet to be wrong is the issue This is a github repo with my code. Thank you
I tried making changes to the slice, the fetchPosts function and the PostsList component but still no outcome.