i need some help organizing my data fetching
on my website, users make posts and each post goes in different categories. There are many pages that need to load different types of categories (e.g. an "all" page which loads the posts of all categories, a "following" page which loads the posts of all categories the user is following, and a specific page for each category that loads all the posts in that category)
Right now, i have a PostFeed.tsx component that does the data fetching (code below)
and each separate page has a different component that fetches the initalPosts (first few posts) (code below)
but the posts stay the same when i navigate to another page, say from "all" to "following" until i reload the page
how should i fix that? should I change how i organize the data fetching?
later on i will need to implement filtering and sorting as well (sort by creation date, score, etc. and filter by tags)
#How to Fetch Data in this Scenario?
17 messages · Page 1 of 1 (latest)
🔎 This post has been indexed in our web forum and will be seen by search engines so other users can find it outside Discord
🕵️ Your user profile is private by default and won't be visible to users outside Discord, if you want to be visible in the web forum you can add the "Public Profile" role in id:customize
✅ You can mark a message as the answer for your post with Right click -> Apps -> Mark Solution
(if you don't see the option, try refreshing Discord with Ctrl + R)
PostFeed.tsx (code for fetching data)
"use client";
import { INFINITE_SCROLL_PAGINATION_RESULTS } from "@/config";
import { ExtendedPost } from "@/types/db";
import { useIntersection } from "@mantine/hooks";
import { useInfiniteQuery } from "@tanstack/react-query";
import axios from "axios";
import { useSession } from "next-auth/react";
import { FC, useEffect, useRef } from "react";
import Post from "../Post";
import Spinner from "../ui/Spinner";
interface PostFeedProps {
initialPosts: ExtendedPost[];
categoryURL?: string;
noPostsMessage: string;
}
const PostFeed: FC<PostFeedProps> = ({
initialPosts,
categoryURL,
noPostsMessage,
}) => {
const lastPostRef = useRef<HTMLElement>(null);
const { ref, entry } = useIntersection({
root: lastPostRef.current,
threshold: 1,
});
const { data: session } = useSession();
const { data, fetchNextPage, isFetchingNextPage } = useInfiniteQuery(
["infinite-query"],
async ({ pageParam = 1 }) => {
const query =
`/api/posts?limit=${INFINITE_SCROLL_PAGINATION_RESULTS}&page=${pageParam}` +
(!!categoryURL ? `&categoryURL=${categoryURL}` : "");
const { data } = await axios.get(query);
return data as ExtendedPost[];
},
{
getNextPageParam: (_, pages) => {
return pages.length + 1;
},
initialData: { pages: [initialPosts], pageParams: [1] },
}
);
PostFeed.tsx Continued: ```js
useEffect(() => {
if (entry?.isIntersecting) {
fetchNextPage(); // Load more posts when the last post comes into view
}
}, [entry, fetchNextPage]);
const posts = data?.pages.flatMap((page) => page) ?? initialPosts;
console.log(posts);
return (
<ul className="flex flex-col col-span-2">
{posts.length > 0 ? (
<>
{posts.map((post, index) => {
const votesAmt = post.votes.reduce((acc, vote) => {
if (vote.type === "UP") return acc + 1;
if (vote.type === "DOWN") return acc - 1;
return acc;
}, 0);
const currentVote = post.votes.find(
(vote) => vote.userId === session?.user.id
);
if (index === posts.length - 1) {
// Add a ref to the last post in the list
return (
<li key={post.id} ref={ref}>
<Post
post={post}
commentAmt={post.comments.length}
category={post.category}
votesAmt={votesAmt}
currentVote={currentVote}
/>
</li>
);
} else {
return (
<Post
key={post.id}
post={post}
commentAmt={post.comments.length}
category={post.category}
votesAmt={votesAmt}
currentVote={currentVote}
/>
);
}
})}
</>
) : (
<div className="flex justify-center items-center w-full h-[100px] text-muted-foreground">
<span>{noPostsMessage}</span>
</div>
)}
{isFetchingNextPage && (
<li className="flex justify-center">
<Spinner className="w-6 h-6" />
</li>
)}
</ul>
);
};
export default PostFeed;
AllFeed.tsx (rendered on the "all" page)
import PostFeed from "@/components/feeds/PostFeed";
import { INFINITE_SCROLL_PAGINATION_RESULTS } from "@/config";
import { db } from "@/lib/db";
const AllFeed = async () => {
const posts = await db.post.findMany({
orderBy: {
createdAt: "desc",
},
include: {
votes: true,
author: true,
comments: true,
category: true,
},
take: INFINITE_SCROLL_PAGINATION_RESULTS,
});
return (
<PostFeed initialPosts={posts} noPostsMessage="There are no posts yet." />
);
};
export default AllFeed;
the most urgent problem is the posts stay the same on navigation to other pages like navigating from "all" to "following" until i reload the page
if i reorganize my logic, i need to make sure i can filter and sort by certain fields later on
right now i only have one api route
looks like this:
import { getAuthSession } from "@/lib/auth";
import { db } from "@/lib/db";
import { z } from "zod";
export async function GET(req: Request) {
const url = new URL(req.url);
const session = await getAuthSession();
let followedCategoriesIds: string[] = [];
if (session) {
const followedCategories = await db.follow.findMany({
where: {
userId: session.user.id,
},
include: {
category: true,
},
});
followedCategoriesIds = followedCategories.map(
(follow) => follow.category.id
);
}
try {
const { limit, page, categoryURL } = z
.object({
limit: z.string(),
page: z.string(),
categoryURL: z.string().nullish().optional(),
})
.parse({
categoryURL: url.searchParams.get("categoryURL"),
limit: url.searchParams.get("limit"),
page: url.searchParams.get("page"),
});
let whereClause = {};
if (categoryURL) {
whereClause = {
category: {
name: categoryURL,
},
};
} else if (session) {
whereClause = {
category: {
id: {
in: followedCategoriesIds,
},
},
};
}
const posts = await db.post.findMany({
take: parseInt(limit),
skip: (parseInt(page) - 1) * parseInt(limit), // skip should start from 0 for page 1
orderBy: {
createdAt: "desc",
},
include: {
category: true,
votes: true,
author: true,
comments: true,
},
where: whereClause,
});
return new Response(JSON.stringify(posts));
} catch (error) {
return new Response("Could not fetch posts", { status: 500 });
}
}
so right now it only loads the posts of the categories i am following
or on a "category" page, it loads the posts of that category
ok i think i figured something out
initialPosts changes, but dataPages doesn't
so i would have to refetch dataPages each time
how do i do that?
how do i make it refetch the data on every navigation (nextjs navigation, not page reload)
const PostFeed: FC<PostFeedProps> = ({
initialPosts,
categoryURL,
userId,
noPostsMessage,
}) => {
const lastPostRef = useRef<HTMLElement>(null);
const { ref, entry } = useIntersection({
root: lastPostRef.current,
threshold: 1,
});
const { data: session } = useSession();
const { data, fetchNextPage, isFetchingNextPage } = useInfiniteQuery(
["infinite-query"],
async ({ pageParam = 1 }) => {
const query =
`/api/posts?limit=${INFINITE_SCROLL_PAGINATION_RESULTS}&page=${pageParam}` +
(!!categoryURL ? `&categoryURL=${categoryURL}` : "") +
(!!userId ? `&userId=${userId}` : "");
const { data } = await axios.get(query);
return data as ExtendedPost[];
},
{
getNextPageParam: (_, pages) => {
return pages.length + 1;
},
initialData: { pages: [initialPosts], pageParams: [1] },
}
);
should i do it like this?