Search code examples
reactjsnext.jsserver-side-renderingclient-sidereact-query

When re-entering same url with Link Component in Next.js, prefetchQuery doesn't fetch data (SSR)


I'm using Next.js and React Query for SSR.

Everything is fine until re-entering the exact dynamic route(e.g. domain/support/guide/faq/[id]).

The expected behavior is rendered query data without fetching as I used SSR for the component.

Please check my code

// PAGE, I pass the query string to the <FAQContent /> Component

export default function FAQDetailPage({ id }: { id: string }) {

  return <FAQContent id={id} category={"faq"} />;
}

export async function getServerSideProps({
  query: { id },
}: {
  query: { id: string };
}) {
  const queryClient = new QueryClient();

  try {
    await queryClient.prefetchQuery(["support", "faq", id], () => {
      return sendRequest({
        method: "get",
        path: `/article/${id}`,
        apiType: "Contents",
      });
    });
  } catch (e) {
    return {
      redirect: {
        destination: "/",
        permanent: false,
      },
    };
  }

  const dehydratedQueryClient = dehydrate(queryClient);

  const targetQuery = dehydratedQueryClient.queries[0];

  const targetQueryState = targetQuery.state as any;

  return {
    props: {
      dehydratedState: {
        ...dehydratedQueryClient,
        queries: [
          {
            ...dehydrate(queryClient).queries[0],
            state: {
              ...targetQueryState,
              data: targetQueryState.data.data,
            },
          },
        ],
      },
      id,
    },
  };
}

// Get id and category from the page, and pass to <SupportBoardContent />

export default function FAQContent({
  id,
  category,
}: {
  id: string;
  category: string;
}) {

  return <SupportBoardContent id={id} category={category} />;
}
// Here is the component where hydrate the dehydrated query

export default function SupportBoardContent({
  id,
  category,
}: {
  id: string;
  category: string;
}) {
  const router = useRouter();

  const { pathname } = router;

  // This is a hook for useQuery.
  const {
    data: boardContent,
    ResponseHandler: ResponseHandlerOfGetBoardContent,
  } = SUPPORT_QUERY.useGetBoardContent({
    id,
    category,
  });

  const { title, contents, memo, videoURL, articleType } =
    boardContent?.targetArticle || {};


  return (
    <SupportLayout>

      {boardContent && (
        <CommonStyled.mainContainer>
          <SupportBreadcrumb title={title} />
          <BoardPageTitle titleStyle="default">
            {getArticleBoardTypeString(articleType)}
          </BoardPageTitle>
          <BoardContent
            title={title}
            annotation={memo}
            desc={contents}
            videoURL={videoURL}
          />
        </CommonStyled.mainContainer>
      )}
    </SupportLayout>
  );
}

When the component mounts on its first, nothings wrong. Everything works I expected. However, There is a <SupportBreadCrumb /> Component.

In <SupportBreadCrumb />, I use Link from Next/link. Whenever I tries to enter the same page with Link Component, the data from useQueryHook is undefined.

id is not undefined, category is not undefined. BUT data is undefined.

I'm struggling with this problem for hours and need some guidance about it.

From this post, I want to know about:

  1. Does using Link from Next.js is not a proper behavior when using SSR? By using Link, I heard that rendering of very first page is SSR condition, but does CSR afterword.

  2. I want to know why useQuery doesn't fetch data, even id is ready for parameter.


Solution


  • Answer to your first question:
    A link component in SSR always calls the getServerSideProps function, and this cannot be prevented. (It won't call when shallow routing is used).

    A trick is to check whether the request to getServerSideProps is from a client side navigation and if so return an empty object for props and handle data fetching on the client.

    Your second question :
    I am not sure what's going on and why your useQuery hook fails to fetch data, but your hydrate/dehydrate logic in your getServerSideProps is strange to me and I did not find any Hydrate component. Maybe this is what is causing your problems.

    I wrote a blog post about using react-query and next.js SSR and I hope it can help you.