Avoid Re-fetching shared data
Answered
Polish posted this in #help-forum

PolishOP
I have the following setup
/profile/[id]
page.js - server
gets [id] and passes it to client component content.js (shared)
content.js fetchs profile data and renders profile card (shared)
/profile/[id]/following
page.js - server
gets [id] and passes it to client component content.js (shared)
content.js fetchs profile data and renders profile card (shared)
Tab renders section for people they are following
Tab fetchs following specific api data
/profile/[id]/followers
page.js - server
gets [id] and passes it to client component content.js (shared)
content.js fetchs profile data and renders profile card (shared)
Tab renders section for people who are following them
Tab fetchs followers specific api data
The issue is since all 3 routes are using the shared content.js all route navigations are refetching the profile data. Is there any way to improve my code so profile data api isn't constantly being refetched
/profile/[id]
page.js - server
gets [id] and passes it to client component content.js (shared)
content.js fetchs profile data and renders profile card (shared)
/profile/[id]/following
page.js - server
gets [id] and passes it to client component content.js (shared)
content.js fetchs profile data and renders profile card (shared)
Tab renders section for people they are following
Tab fetchs following specific api data
/profile/[id]/followers
page.js - server
gets [id] and passes it to client component content.js (shared)
content.js fetchs profile data and renders profile card (shared)
Tab renders section for people who are following them
Tab fetchs followers specific api data
The issue is since all 3 routes are using the shared content.js all route navigations are refetching the profile data. Is there any way to improve my code so profile data api isn't constantly being refetched
Answered by adam.birds
The provider is the way forward. Here is an example of my auth provider which also fetches user data etc:
https://github.com/adb-software-solutions/debuglife/blob/main/debuglife-frontend/src/context/AuthContext.tsx
And then I can just import
https://github.com/adb-software-solutions/debuglife/blob/main/debuglife-frontend/src/context/AuthContext.tsx
And then I can just import
useAuth
anywhere to access the user data.25 Replies

PolishOP
Also the shared profile data is fetched using useEffect

Round sardinella
You should look into React Query. This sort of issue is one of the main focuses of that library.
https://tanstack.com/query/latest/docs/framework/react/overview
https://tanstack.com/query/latest/docs/framework/react/overview
Instead of querying inside a useEffect, React Query provides hooks where you define your data fetching logic and unique keys that determine if a query should be refetched.
The data returned by each query is cached using the unique key you provide and can be manually refetched if needed.

@Round sardinella You should look into React Query. This sort of issue is one of the main focuses of that library.
<https://tanstack.com/query/latest/docs/framework/react/overview>

PolishOP
Any way without an additional library? Any way to preserve the useState?

Round sardinella
Without third party code, you'd just have to set up a context to be shared across your component tree

@Round sardinella Without third party code, you'd just have to set up a context to be shared across your component tree

PolishOP
Could you provide an example of how context would be implemented? I don't want the data cached per say. So like if the page is reloaded refetching is fine. Just want to avoid the refetching during navigation

Cape lion
Im not sure if i understand the problem correctly, but cant you move profile data fetching to layout.tsx and pass it down as prop?

@Cape lion Im not sure if i understand the problem correctly, but cant you move profile data fetching to layout.tsx and pass it down as prop?

PolishOP
Maybe? How does that get passed down to the children?
export default function RootLayout({ children }) {
return (
<div>{children}</div>
);
}

Cape lion
I think the only way would be to use createElement...
but as i read
so as @Round sardinella said,
i think you need to create provider in layout
but im not 100% sure about it 😅
but as i read
Using cloneElement is uncommon and can lead to fragile code.
so as @Round sardinella said,
i think you need to create provider in layout
export default async function ProfileLayout({ children, params }) {
const { id } = params;
const profileData = await fetchProfileData(id);
return <ProfileProvider profileData={profileData}>{children}</ProfileProvider>;
}
but im not 100% sure about it 😅

@Cape lion I think the only way would be to use createElement...
but as i read
Using cloneElement is uncommon and can lead to fragile code.
so as <@84365543947112448> said,
i think you need to create provider in layout
tsx
export default async function ProfileLayout({ children, params }) {
const { id } = params;
const profileData = await fetchProfileData(id);
return <ProfileProvider profileData={profileData}>{children}</ProfileProvider>;
}
but im not 100% sure about it 😅

PolishOP
Still slightly confused how I would access the provider in the children

Round sardinella
With React context, you create and define a context in a provider component and any children of that component can access it with
useContext

@Cape lion I think the only way would be to use createElement...
but as i read
Using cloneElement is uncommon and can lead to fragile code.
so as <@84365543947112448> said,
i think you need to create provider in layout
tsx
export default async function ProfileLayout({ children, params }) {
const { id } = params;
const profileData = await fetchProfileData(id);
return <ProfileProvider profileData={profileData}>{children}</ProfileProvider>;
}
but im not 100% sure about it 😅

PolishOP
Also this wouldn't work if the fetching is being done on the server side. It needs to be client side fetching

The provider is the way forward. Here is an example of my auth provider which also fetches user data etc:
https://github.com/adb-software-solutions/debuglife/blob/main/debuglife-frontend/src/context/AuthContext.tsx
And then I can just import
https://github.com/adb-software-solutions/debuglife/blob/main/debuglife-frontend/src/context/AuthContext.tsx
And then I can just import
useAuth
anywhere to access the user data.Answer
Just found this which I need this to access the params
let params = useParams();

@Polish Also this wouldn't work if the fetching is being done on the server side. It needs to be client side fetching

Cape lion
Why does it need to be client side?

@Cape lion Why does it need to be client side?

PolishOP
1. Nature of the API
2. Performance
2. Performance

Australian Freshwater Crocodile
Do you want to avoid making the request (network) or you want to avoid repeating the getDataFunction() logic everywhere?
There’s different approaches depending on what you need/try to achieve.
If what you want is avoid is to make several requests when you know for a fact they’re all gonna be the same call then wrap your fetching function (either it’s a Database call, fetch, whatever) in Next.js unstable_cache (that’s actually pretty stable lol).
If you want to keep the components clean and it’s gonna be all client anyway then use context and abstract the logic to a custom hook.
If all you want is having access to the params in the client side just use useParams().
There’s different approaches depending on what you need/try to achieve.
If what you want is avoid is to make several requests when you know for a fact they’re all gonna be the same call then wrap your fetching function (either it’s a Database call, fetch, whatever) in Next.js unstable_cache (that’s actually pretty stable lol).
If you want to keep the components clean and it’s gonna be all client anyway then use context and abstract the logic to a custom hook.
If all you want is having access to the params in the client side just use useParams().
There’s no such thing as Context in the server side tho, if you put something in Context you’ll only be able to access it on client components
And also if you’re using React 19, if you’re doing the fetching on mount inside a use Effect you could instead trigger the fetching promise in the server component (page.js) and pass the promise down to the client component, then access to the promise value with the hook use(promise).
This will suspend your component while it’s waiting for the data and you can wrap it in suspense for a nice loading state.
If this fetcher function is wrapped in unstable_cache and it’s called in a different component and it’s already solved then it’ll be instantly
This will suspend your component while it’s waiting for the data and you can wrap it in suspense for a nice loading state.
If this fetcher function is wrapped in unstable_cache and it’s called in a different component and it’s already solved then it’ll be instantly

@adam.birds The provider is the way forward. Here is an example of my auth provider which also fetches user data etc:
https://github.com/adb-software-solutions/debuglife/blob/main/debuglife-frontend/src/context/AuthContext.tsx
And then I can just import `useAuth` anywhere to access the user data.

PolishOP
This clarified it for me. Just needed to see an example to know how to properly implement it.