Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nextjs getServerSideProps show loading

I am using getServerSideProps in pages/post/index.js:

import React from "react";
import Layout from "../../components/Layout";

function Post({ post }) {
  console.log("in render", post);
  return (
    <Layout title={post.name}>
      <pre>{JSON.stringify(post, undefined, 2)}</pre>
    </Layout>
  );
}

export async function getServerSideProps({ query }) {
  return fetch(
    `${process.env.API_URL}/api/post?id=${query.id}`
  )
    .then(result => result.json())
    .then(post => ({ props: { post } }));
}

export default Post;

When I directly load /post/2 it works as expected but when I go from /posts to /post/2 by clicking on a link:

<Link
  as={`/post/${post.id}`}
  href={`/post?id=${post.id}`}
>

It looks like nothing happens for 2 seconds (the api delay) and then the content shows. I can see in the network tab that _next/data/development/post/9.json is being loaded by fetchNextData.

I would like to show a loading spinner when I move from one route to another using next/Link but I can't find any documentation on getServerSideProps that allows me to do this.

When I directly go to /post/:id I'd like the data to be fetched server side and get a fully rendered page (works) but when I then move to another route the data should be fetched from the client (works). However; I would like to have a loading indicator and not have the UI freeze up for the duration of the data request.

like image 800
HMR Avatar asked Mar 19 '20 10:03

HMR


People also ask

Does getServerSideProps cache?

You can use caching headers ( Cache-Control ) inside getServerSideProps to cache dynamic responses.

What is difference between getStaticProps and getServerSideProps?

getStaticProps(): A method that tells the Next component to populate props and render into a static HTML page at build time. getServerSideProps(): A method that tells the Next component to populate the props and render into a static HTML page at run time.

Is next JS server-side rendering?

Next. js has two forms of pre-rendering: Static Generation and Server-side Rendering.

What is the difference between getstaticprops and getserversideprops in next JS?

In Next js, data fetching is a strategy. Next.js uses getStaticProps or getServerSideProps to enhance different fetching capabilities. Even though both are used to pre-render data, the former uses static generation whilst the latter uses server side rendering. Hmm... ok but do I need page pre-rendering?

How to use getserversideprops in non-page components?

You cannot use getServerSideProps in non-page components. You can either pass the prop from Home to HomeSection or create a context so the value can be available globally from the component tree getServerSideProps can only be exported from a page. You can’t export

What is getserversideprops in react?

Meaning getServerSideProps pre-renders the page on each request using the data it retrieves from the server. Table of content 1. Reactjs Client-side rendered HTML content 2.

Can I use ReactJS and nextjs on the same page?

You can use Reactjs and Nextjs on the same page. However, the project should follow the Nextjs folder structure and architecture. Only the content rendered by Nextjs will be hydrated. Below is a simple application page that we have converted to server-side render with the getServerSideProps method.


Video Answer


4 Answers

Here is an example using hooks.

pages/_app.js

    import Router from "next/router";

    export default function App({ Component, pageProps }) {
      const [loading, setLoading] = React.useState(false);
      React.useEffect(() => {
        const start = () => {
          console.log("start");
          setLoading(true);
        };
        const end = () => {
          console.log("finished");
          setLoading(false);
        };
        Router.events.on("routeChangeStart", start);
        Router.events.on("routeChangeComplete", end);
        Router.events.on("routeChangeError", end);
        return () => {
          Router.events.off("routeChangeStart", start);
          Router.events.off("routeChangeComplete", end);
          Router.events.off("routeChangeError", end);
        };
      }, []);
      return (
        <>
          {loading ? (
            <h1>Loading...</h1>
          ) : (
            <Component {...pageProps} />
          )}
        </>
      );
    }
like image 85
HMR Avatar answered Oct 21 '22 05:10

HMR


You can use nprogress in your _app.js

import NProgress from 'nprogress';
import "nprogress/nprogress.css";
import Router from 'next/router';

NProgress.configure({
  minimum: 0.3,
  easing: 'ease',
  speed: 800,
  showSpinner: false,
});

Router.events.on('routeChangeStart', () => NProgress.start());
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());

or dynamic import to _app.js to reduce bundle size

ProgessBar.js

import Router from 'next/router';
import NProgress from 'nprogress';
import "nprogress/nprogress.css";

NProgress.configure({
    minimum: 0.3,
    easing: 'ease',
    speed: 500,
    showSpinner: false,
});

Router.events.on('routeChangeStart', () => NProgress.start());
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());

export default function () {
    return null;
}

_app.js

import dynamic from 'next/dynamic';
const ProgressBar = dynamic(() => import('components/atoms/ProgressBar'), { ssr: false });

const App = () => {
   ...
   return <>
       ...
       <ProgressBar />
   </>
}

Ps: If you want to change color of progress bar, you can override in global css, something like this

#nprogress .bar {
    background: #6170F7 !important;
    height: 3px !important;
}
like image 32
iamhuynq Avatar answered Oct 21 '22 04:10

iamhuynq


How about simply adding a component level loading state to Post (vs. adding a loader on App Level for every route change since some route changes might not require server side rendering).

Setting the isLoading state to true when the relevant query param changes, in this case the post id, and setting the state to false once the props, in this case the post data, updated.

Along these lines:

pages/post/index.js:

import React from "react";
import Layout from "../../components/Layout";
import { useRouter } from 'next/router';

function Post({ post }) {
  const router = useRouter();
  const [isLoading, setIsLoading] = useState(false);
  
  // loading new post
  useEffect(()=> {
   setIsLoading(true);
  }, [router.query?.id]);
  
  // new post loaded
  useEffect(()=> {
   setIsLoading(false)
  }, [post]);

  return (
    <>
    {isLoading ? (
      <h1>Loading...</h1>
     ) : (
      <Layout title={post.name}>
       <pre>{JSON.stringify(post, undefined, 2)}</pre>
      </Layout>
    )}
    </> 
  );
}

export async function getServerSideProps({ query }) {
  return fetch(
    `${process.env.API_URL}/api/post?id=${query.id}`
  )
    .then(result => result.json())
    .then(post => ({ props: { post } }));
}

export default Post;
like image 1
Kevin K. Avatar answered Oct 21 '22 06:10

Kevin K.


You can create a custom hook:

usePageLoading.ts

import Router from 'next/router';
import { useEffect, useState } from 'react';

export const usePageLoading = () => {
  const [isPageLoading, setIsPageLoading] = useState(false);

  useEffect(() => {
    const routeEventStart = () => {
      setIsPageLoading(true);
    };
    const routeEventEnd = () => {
      setIsPageLoading(false);
    };

    Router.events.on('routeChangeStart', routeEventStart);
    Router.events.on('routeChangeComplete', routeEventEnd);
    Router.events.on('routeChangeError', routeEventEnd);
    return () => {
      Router.events.off('routeChangeStart', routeEventStart);
      Router.events.off('routeChangeComplete', routeEventEnd);
      Router.events.off('routeChangeError', routeEventEnd);
    };
  }, []);

  return { isPageLoading };
};

and then inside your App component use it: _app.js

import Router from "next/router";
import { usePageLoading } from './usePageLoading';

export default function App({ Component, pageProps }) {
  const { isPageLoading } = usePageLoading();

  return (
    <>
      {isPageLoading ? (
        <h1>Loading...</h1>
      ) : (
        <Component {...pageProps} />
      )}
    </>
   );
}
like image 1
marko424 Avatar answered Oct 21 '22 06:10

marko424