#How to Fetch Data in this Scenario?

17 messages · Page 1 of 1 (latest)

rose flume
#

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)

candid sierraBOT
#

🔎 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)

rose flume
#

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

rose flume
#

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?