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?
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.
Alternatively, if you are not using API routes to fetch data, then the fetch() API can be used directly in getStaticProps to fetch data.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With