Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
July 20, 2022 04:17 pm GMT

React Query v4 SSR in Next JS

SSR data fetching + caching mechanism is a bit tricky in next js.

In this article, we will learn how to improve initial load time via SSR and have a high speed client side navigation with the help of CSR and React Query.

We will create a blog app using the JSON Placeholder API.

We are going to see just important sections here. To see the full source code checkout the github repo. You can also check the Live demo to get a better sight. The React Query devtools is available in this demo so you can check the cache flow.

Table of contents

  • 1. Create a new project
  • 2. Setup Hydration
  • 3. Prefetching and dehydrate data
  • 4. Shallow Routing
  • 5. with-CSR HOC
  • 6. handle 404 status code
  • 7. Conclusion
  • 8. Refrences

1. Create a new project

First, create a nextjs project:

yarn create next-app blog-appornpx create-next-app blog-app

Let's install React Query and Axios:

yarn add @tanstack/react-query axiosornpm install @tanstack/react-query axios

2. Setup Hydration

Due to the react query documents we set up hydration in _app.js :

//pages/_app.jsimport { useState } from 'react';import { Hydrate, QueryClient, QueryClientProvider } from '@tanstack/react-query';import { config } from 'lib/react-query-config';function MyApp({ Component, pageProps }) {    // This ensures that data is not shared     // between different users and requests    const [queryClient] = useState(() => new QueryClient(config))    return (        <QueryClientProvider client={queryClient}>            // Hydrate query cache            <Hydrate state={pageProps.dehydratedState}>                <Component  {...pageProps} />            </Hydrate>        </QueryClientProvider>    )}export default MyApp;

3. Prefetching and dehydrate data

Before we continue note that In v3, React Query would cache query results for a default of 5 minutes, then manually garbage collect that data. This default was applied to server-side React Query as well. This lead to high memory consumption and hanging processes waiting for this manual garbage collection to complete. In v4, by default the server-side cacheTime is now set to Infinity effectively disabling manual garbage collection (the NodeJS process will clear everything once a request is complete).

Now we need to prefetch data and dehydrate queryClient in getServerSideProps method :

//pages/posts/[id].jsimport { getPost } from 'api/posts';import { dehydrate, QueryClient } from '@tanstack/react-query';export const getServerSideProps = async (ctx) => {    const { id } = ctx.params;    const queryClient = new QueryClient()    // prefetch data on the server    await queryClient.prefetchQuery(['post', id], () => getPost(id))    return {        props: {            // dehydrate query cache            dehydratedState: dehydrate(queryClient),        },    }}

From now on we can easily use this prefetched data in our page, without passing any data by props.

Just to be clear lets take a look at the implementation of getPost method and usePost hook :

//api/posts.jsimport axios from 'lib/axios';export const getPost = async id => {    const { data } = await axios.get('/posts/' + id);    return data;}
//hooks/api/posts.jsimport { useQuery } from '@tanstack/react-query';import * as api from 'api/posts';export const usePost = (id) => {    return useQuery(['post', id], () => api.getPost(id));}

Now we can use this usePost hook to get post data.

//pages/posts/[id].jsimport { useRouter } from 'next/router';import { usePost } from 'hooks/api/posts'import Loader from 'components/Loader';import Post from 'components/Post';import Pagination from 'components/Pagination';const PostPage = () => {    const { query: { id } } = useRouter();    const { data, isLoading } = usePost(id);    if (isLoading) return <Loader />    return (        <>            <Post id={data.id} title={data.title} body={data.body} />            <Pagination id={id} />        </>    )}// getServerSideProps implementation ...// We talked about it in section 2

4. Shallow Routing

We want to manage our data fetching and caching mechanism just in the client so we need to use shallow = true prop in the Link component for navigating between post pages to prevent calling getServerSideProps each time. This means that getServerSideProps method will only call when the users directly hit the URL of the post and not in the client side navigation within the app.

We have a Pagination component to navigate between pages, so we use shallow = true here :

//components/Pagination.jsximport Link from 'next/link';function PaginationItem({ index }) {    return (        <Link className={itemClassName} href={'/posts/' + index} shallow={true}>            {index}        </Link>    )}export default PaginationItem;

P.S : We used the new link component in nextjs v12.2 so we didn't need to use <a> tag here.

5. with-CSR HOC

At this time nextjs v12.2 shallow routing only works for URL changes in the current page. nextjs shallow routing caveats
this means if you navigate from /posts/10 to /posts/15 with shallow = true the getServerSideProps won't call but if you navigate from /home to /posts/15 the getServerSideProps is called even you use shallow routing and this will fetch unnecessary data even if it's available in the cache.

I found a work around that checks if this request to getServerSideProps is a client side navigation request or not. If it was, then returns an empty object for props and prevents fetching data on the server.
we can't prevent calling getServerSidePropswhen navigating between different pages but we can prevent fetching unnecessary data in the getServerSideProps.

Here is withCSR HOC implementation :

//HOC/with-CSR.jsexport const withCSR = (next) => async (ctx) => {    // check is it a client side navigation     const isCSR = ctx.req.url?.startsWith('/_next');    if (isCSR) {        return {            props: {},        };    }    return next?.(ctx)}

Now we should wrap our getServerSideProps with this HOC.

//pages/posts/[id].jsimport { getPost } from 'api/posts';import { dehydrate, QueryClient } from '@tanstack/react-query';import { withCSR } from 'HOC/with-CSR'export const getServerSideProps = withCSR(async (ctx) => {    const { id } = ctx.params;    const queryClient = new QueryClient()    await queryClient.prefetchQuery(['post', id], () => getPost(id))    return {        props: {            dehydratedState: dehydrate(queryClient),        },    }})

If we navigate from different pages to post pages, getServerSideProps won't fetch any data and it just returns an empty object for props.

6. Handle 404 status code

While Next.js renders an error page if a post is not available, it doesn't actually respond with an error status code.

This means that while you can be viewing a 404 error, the page is actually responding with a 200 code. To search engines, this essentially translates to: "Everything went fine and we found the page". Rather than actually responding with a 404, which tells search engines that page doesn't exist.

To resolve this issue, let's take a look at getServerSideProps again :

const Page = ({ isError }) => {    //show custom error component if there is an error    if (isError) return <Error />    return <PostPage />}export const getServerSideProps = withCSR(async (ctx) => {    const { id } = ctx.params;    const queryClient = new QueryClient(config)    await queryClient.prefetchQuery(['post', id], () => getPost(id));    //get post state    const state = queryClient.getQueryState(['post', id]);    const isError = !!state.error;    //if there is an error we will change response status code    if (isError) {        ctx.res.statusCode = state.error.response.status    }    return {        props: {            //also passing down isError state to show a custom error component.            isError,            dehydratedState: dehydrate(queryClient),        },    }})export default Page;

7. Conclusion

We setup a caching mechanism with the ability to prefetch data on the server in SSR context. We also learned how to use shallow routing for faster client side navigation.

Here is the live demo of our implementation and the github repository for source code.
As well I had been added React Query devtools into production for you to understand thoroughly what is going under the hood.

I would like to extend my sincere thanks to @aly3n.

8. Refrences

  1. JSON Placeholder API
  2. React Query setup hydration
  3. React Query no manual garbage collection server side
  4. nextjs shallow routing caveats
  5. Prevent data fetching in getServerSideProps on client-side navigation
  6. respond with a 404 error in Next.js
  7. Project source code
  8. Live demo

Original Link: https://dev.to/arianhamdi/react-query-v4-ssr-in-next-js-2ojj

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To