Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Next.js Fetch and Maintain global data for all components

In my Next.js app, I need to make an API call that will return a JSON response which needs to be used by all the components. Hence I will avoid multiple calls for the same API in my components. What is the proper way to achieve this?

like image 856
Jayanth Avatar asked Jun 24 '20 13:06

Jayanth


People also ask

Can I use fetch in NextJS?

Data fetching in Next. js allows you to render your content in different ways, depending on your application's use case. These include pre-rendering with Server-side Rendering or Static Generation, and updating or creating content at runtime with Incremental Static Regeneration.

Can I use fetch in getStaticProps?

Alternatively, if you are not using API routes to fetch data, then the fetch() API can be used directly in getStaticProps to fetch data.


Video Answer


2 Answers

Depends on the scope of the application.
If it's a large one, you should probably adopt a state manager like Redux, as Moshin Amjad says.

If it's a smaller app, you could try to manage it with the React context APIs.
I'll make an example in the simplest way, using functional components, levering getStaticProps instead of getInitialProps in order to get static generated pages.

Start creating a simple context

libs/context.js

import React from "react";
export const Context = React.createContext();

Then populate a useState hook (or better, a useReducer depends on how the data is structured) with the data coming from getStaticProps (or getInitialProps) and then pass it to the Context Provider.

pages/index.js

import React from 'react'
import { Context } from "../libs/context.js"

import Title from "../components/Title"
import Button from "../components/Button"

// data will be populated at build time by getStaticProps()

function Page({ data }) {
    const [ context, setContext ] = React.useState(data)
    return (
        <Context.Provider value={[context, setContext]}>
            <main>
                <Title />
                <Button />
            </main>
        </Context.Provider>
    )
}

export async function getStaticProps(context) {

  // fetch data here
  const data = await fetchData()

  // Let's assume something silly like this:
  // {
  //     buttonLabel: 'Click me to change the title',
  //     pageTitle: 'My page'
  // }
  
  return {
    props: {
       data
    }, // will be passed to the page component as props
  }
}

And finally consume it (or change it!) in any child of the provider.

components/Title.js

import React, { useContext } from "react"
import { Context } from "./Context"

export default function MyComponent() {
   const [context, setContext] = useContext(Context)
   
   return (
       <h1>{context.pageTitle}</h1>
   )
}

components/Button.js

import React, { useContext } from "react"
import { Context } from "./Context"

export default function MyComponent() {
   const [context, setContext] = useContext(Context)
   
   function changeTitle() {
      preventDefault();
      setContext(oldContext => ({ 
          ...oldContext, 
          pageTitle: 'New page title!' 
      }))
   }

   return (
       <div>
          <button onClick={changeTitle}>{context.buttonLabel}</button>
       </div>
   )
}

It's untested but you get the idea.
Eventually you could move the context provider in a High Order Component to wrap every page with, or even in pages/_app.js if you need the data at a higher level.

Remember, if the app scales up, you should consider something like Redux.

like image 84
Stefano Giometti Avatar answered Dec 09 '22 17:12

Stefano Giometti


Use redux to store your state in global store so it can be accessed by multiple components this is common pattern for your kind of use case. There is also next-wrapper for it here is the link next-redux-wrapper redux

like image 28
Mohsin Amjad Avatar answered Dec 09 '22 18:12

Mohsin Amjad