I am working on a project which have multiple routes like /profile /admin /home
I am fetching posts from backend using Redux Toolkit query in profile page and getting posts successfully but when I go to another page and come back to profile and dispatch again the posts in the states
import { Link, useLocation } from "react-router-dom";
import Tweets from "../components/Tweets";
import { FaEdit } from "react-icons/fa";
import { useGetUserDetailsQuery } from "../slices/userApiSlice";
import ProfileRightBar from "../components/ProfileRightBar";
import { toast } from "react-toastify";
import Spinner from "../components/Spinner";
import { useEffect, useState, useRef } from "react";
import { useGetUserPostsQuery } from "../slices/postApiSlice";
import { useDispatch, useSelector } from "react-redux";
import { updateUserPosts } from "../slices/postSlice";
const ProfileScreen = () => {
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const lastPostRef = useRef();
const location = useLocation();
const dispatch = useDispatch();
const username = location.pathname.split("/")[2];
const { data: posts, isFetching } = useGetUserPostsQuery();
const { userPosts } = useSelector((state) => state.posts);
useEffect(() => {
toast.error(error?.data?.message);
}, [error]);
useEffect(() => {
if (posts) {
if (posts.length === 0) {
setHasMore(false);
} else {
dispatch(updateUserPosts(posts));
}
}
}, [posts]);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting && !isFetching && hasMore) {
// Fetch more posts when last post is intersecting
setPage((prevPage) => prevPage + 1);
}
},
{ threshold: 1 }
);
if (lastPostRef.current) {
observer.observe(lastPostRef.current);
}
// Cleanup the observer
return () => observer.disconnect();
}, [isFetching]);
if (isLoading) {
return <Spinner />;
} else if (error) {
return null;
} else {
return (
<div className="w-full h-screen overflow-scroll">
<div className="flex">
<div className="border-r border-lightgray flex-[0.7] h-screen overflow-auto">
<Tweets posts={userPosts} />
<div ref={lastPostRef}></div>
</div>
</div>
</div>
);
}
};
export default ProfileScreen;
the main problem is when i come back to profile page it dispatch again the same posts in my array
the updateUserPost slice is here
const initialState = {
posts: [],
userPosts: [],
};
const postSlice = createSlice({
name: "posts",
initialState,
reducers: {
updateUserPosts: (state, action) => {
state.userPosts.push(...action.payload);
},
},
});
2
Answers
That is how effects run. See what happens when you come back to the page is that the component is mounted again and the server call is made again. After the server call
dispatch(updateUserPosts(posts))
is run again.I guess in
updateUserPosts
, you are appending the posts passed. You should ideally only add the posts if they already do not exist. You can usefilter()
orSet
to get unique elements.Redux Toolkit Query (RTK Query) takes care of storing fetched data for you, so you may not have to create extra storage for posts unless you have a special reason for it.
You are getting posts with useGetUserPostsQuery() and then saving them again in your Redux store with updateUserPosts(posts) even though it’s not necessary. This may not be needed because RTK Query’s caching system should take care of loading and saving your posts.
Every time you go to your profile, the useGetUserPostsQuery() could be getting data again because of how it’s set up or because it’s not being cached properly.
You don’t have to keep the posts you get in your Redux unless you’re changing or combining the data in a way that RTK Query can’t do.
You can use filter() or Set to get distinct posts.