Data Fetching In React App with NextJS
Adam C. |

Data Fetching is no doubt the most important part of React App, because React is data-driven. There are three types of Data Fetching:

  1. Static Generation
  2. Server-side Rendering
  3. Client-side Rendering
Photo by Markus Spiske

Static Generation

Static Generation means the data is fetched at build time. For Next.js versions 9.3 and up,  there are two unique functions, getStaticProps and getStaticPaths, which are running at build time to generate  data saved in .next/static folder by default. And then those data (HTML and JSON files) will be directly loaded when the page loads, which will certainly increase the performance.  The getStaticPaths is used to specify dynamic routes, and define a list of paths that have to be rendered to HTML at build time.  We don't use getStaticPaths in DeNiApps, so let's skip this for now, and focus on getStaticProps.  To use it, we can simply add the function like below:

export async function getStaticProps() {
  let data = null;
  try {
    const res = await fetch("https://api.tvmaze.com/search/shows?q=nba");
    data = await res.json();
  } catch (err) {
    console.log(err);
  }

  return {
    props: {
      shows: data ? data.map((entry) => entry.show) : [],
    },
  };
}

Then the data retrieved from API will be passed to the page component as props. 

Server-side Rendering

Server-side Rendering means the data is fetched on each request before rendering the page. In the React's early day, somewhere in 2015,  people called this Universal Rendering:

With the help of server side rendering the first rendering is never empty and performance is better.

At that time, people used ReduxAsyncConnect, later ReductConnect, and then another popular one named redial, and more. But none of them is as easy as NextJS. With NextJS, you only need to add getServerSideProps function into your pages,  the function looks the same as getStaticProps. For example:

export async function getServerSideProps() {
  let posts = [];
  try {
    const result = await getPublicPosts();
    posts = result.data.data;
  } catch (error) {
    console.log(error);
  }

  return {
    props: { posts: posts }
  };
}

Client-side Rendering

Client-side Rendering means the data is fetched after the page mounted. This is very common in React App when people don't care about Server-side Rendering. Also, in some cases, you may not be able to do Server-side, for example, when the data is only available to the user session, and the user session is saved on the client-side only, like using LocalStorage. In the Object-Oriented way, we can do the data fetching inside, ComponentDidMount, and in a functional way, since React.hook is available after 16.8, we can use useEffect, For example:

async componentDidMount() {
    const ret = await getTags(this.props.accessToken);

    const allTags = ret.data.data;
    const tagsInputOptions = allTags.map((item) => ({
      value: item.slug,
      text: item.name,
      key: item._id,
    }));

    const newAllOptions = {
      ...this.state.allOptions,
      tags: tagsInputOptions,
    };

    this.setState({ allOptions: newAllOptions });
  }

Or:

  useEffect(() => {
    fetchList();
    setTimeout(() => {
      setMessage("");
    }, 3000);
  }, []);

Final Notes

  • getStaticProps /getServerSideProps has to be used in the page level, not in the component level.  So you should use it in any pages (i.e., under pages folder in NextJS.)
  • In DEV env, (i.e., npm run dev) the getStaticProps is executing like getServerSideProps.
  • getStaticProps is only executed at build time, so it may not work for you, your page is dynamically changed, for example, in DeNiApps, the blog list page have to use getServerSideProps instead of getStaticProps, because we don't want to rebuild the website every time we publish a new post.
  • both getStaticProps/getServerSideProps get context parameter pass in, which includes ‘params’, ‘query’, etc. information, which you may need for the data fetching. Click the links to view details.
  • In NextJS 9.2 or older, we can use getInitialProps for server-side rendering. We can still use getInitialProps in NextJS9.3 or newer, especially, if you want to do server-side data fetching in _app.js, because we cannot do getStaticProps or getServerSideProps in _app.js.

Bonus Note

The parameter for getInitailProps is different depending on whether we call it in page level or in _app.js, as the example below:

//in pages/home.js
Home.getInitialProps = async ({ req }) => {
  const md = new MobileDetect(req.headers["user-agent"]);
  const isMobileFromSSR = !!md.mobile();

  return {
    isMobileFromSSR,
    deviceInfo: {
      mobile: md.mobile(),
      tablet: md.tablet(),
      os: md.os(),
      userAgent: md.userAgent(),
    },
  };
};
//in pages/_app.js
deniApp.getInitialProps = async (appContext) => {
  const agent = appContext.ctx.req
    ? appContext.ctx.req.headers["user-agent"]
    : "";
  const md = new MobileDetect(agent);
  const isMobileFromSSR = !!md.mobile();
  return { isMobileFromSSR };
};

As you see in page level, it's context.req, but in _app.js, it's appContex.ctr.req.