Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use getStaticProps in _app.js for showing data fetched from api in every pages in Next.js

To be specific I am showing data on the navbar from the API endpoint. And I want to use getStaticProps to get those data on all the pages. But the problem is I need to do getStaticProps on all the pages.

Can we do something like this and get data on all the pages as props?

//pages/_app.js
function MyApp({ Component, pageProps, navs }) {
  return (
    <>
      <Component {...pageProps} navs={navs} />
    </>
  );
}
export async function getStaticProps() {
  const res = await api.get("/api/v1/navs");
  const navs = res.data;
  return {
    props: {
      navs,
    },
    // revalidate: 60, // In seconds
  };
}

export default MyApp;

What could be the alternative way of doing this? Is there a way to manage a global state, so that it is used by all the pages? I don't think we can use Context API to provide data to all the pages too.

like image 585
yathomasi Avatar asked May 21 '21 15:05

yathomasi


People also ask

Can I use fetch in getStaticProps?

This means that instead of fetching an API route from getStaticProps (that itself fetches data from an external source), you can write the server-side code directly in getStaticProps . Take the following example. An API route is used to fetch some data from a CMS.

Can you use getServerSideProps in _APP js?

getServerSideProps does not work in _app. js . see docs. you could use the older getInitialProps in your custom app component but then the automatic static optimisation is disabled, which is something Next.

What is getStaticProps in next JS?

getStaticProps: It's an async function that we export from the page component, used to generate data on the build time. It fetches the data and generates the HTML pages on our server and it caches it.

How do I redirect from getStaticProps?

You can achieve this by just switching the function getStaticProps to getServerSideProps . The entire function will execute on the server and your redirection will happen. e.g.


Video Answer


1 Answers

Problem

NextJS doesn't currently support lifecycle methods in _app.js.

Solution

Since the above isn't supported yet, you'll want to create a reusable getStaticProps function and export it from all pages. Unfortunately, this does mean some WET code; however, you can reduce some boilerplate by creating a HOC which wraps the page and also exports a getStaticProps function.

Alternatives

You can use getInitialProps within the _app.js file. Unfortunately, this disables automatic static optimization across the entire application.

Demo

Edit Static Optimized List

Code

components/Navigation

import * as React from "react";
import Link from "next/link";
import { nav, navItem } from "./Navigation.module.css";

const Navigation = () => (
  <div className={nav}>
    {[
      { title: "Home", url: "/" },
      { title: "About", url: "/about" },
      { title: "Help", url: "/help" }
    ].map(({ title, url }) => (
      <div className={navItem} key={title}>
        <Link href={url}>
          {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
          <a>{title}</a>
        </Link>
      </div>
    ))}
  </div>
);

export default Navigation;

components/UsersList

import * as React from "react";
import isEmpty from "lodash.isempty";
import { noUsers, title, userList, user } from "./UsersList.module.css";

const UsersList = ({ error, users, retrieved }) =>
  !retrieved ? (
    <p>Loading...</p>
  ) : !isEmpty(users) ? (
    <div className={userList}>
      <h1 className={title}>Statically Optimized User List</h1>
      {users.map(({ name }) => (
        <div className={user} key={name}>
          {name}
        </div>
      ))}
    </div>
  ) : (
    <div className={noUsers}>{error || "Failed to load users list"}</div>
  );

export default UsersList;

containers/withUsersList

import * as React from "react";
import axios from "axios";
import UsersList from "../../components/UsersList";

/**
 * A HOC that wraps a page and adds the UserList component to it.
 *
 * @function withUsersList
 * @param Component - a React component (page)
 * @returns {ReactElement}
 * @example withUsersList(Component)
 */
const withUsersList = (Component) => {
  const wrappedComponent = (props) => (
    <>
      <UsersList {...props} />
      <Component {...props} />
    </>
  );

  return wrappedComponent;
};

export const getStaticProps = async () => {
  try {
    const res = await axios.get("https://jsonplaceholder.typicode.com/users");

    return {
      props: {
        retrieved: true,
        users: res.data,
        error: ""
      }
    };
  } catch (error) {
    return {
      props: {
        retrieved: true,
        users: [],
        error: error.toString()
      }
    };
  }
};

export default withUsersList;

pages/_app.js

import * as React from "react";
import Navigation from "../components/Navigation";

const App = ({ Component, pageProps }) => (
  <>
    <Navigation />
    <Component {...pageProps} />
  </>
);

export default App;

pages/about

import withUsersList, { getStaticProps } from "../containers/withUsersList";

const AboutPage = () => <div>About Us.</div>;

export { getStaticProps };

export default withUsersList(AboutPage);

pages/help

import withUsersList, { getStaticProps } from "../containers/withUsersList";

const HelpPage = () => <div>Find Help Here.</div>;

export { getStaticProps };

export default withUsersList(HelpPage);

pages/index

import withUsersList, { getStaticProps } from "../containers/withUsersList";

const IndexPage = () => <div>Hello World.</div>;

export { getStaticProps };

export default withUsersList(IndexPage);
like image 116
Matt Carlotta Avatar answered Oct 21 '22 09:10

Matt Carlotta