Fetching data Using Next.js and organizing project
Answered
African Slender-snouted Crocodil… posted this in #help-forum
African Slender-snouted CrocodileOP
Hey guys, im new to Next.js came from React where i was using Context API and UseEffect to fetch data, next is trully getting me confused of how to manage my data fetching, fetching the data right inside the component that i need sounds like creating a huge mess on long term, so how can I actually organize my project and where is the best to fetch data so it doesnt get a mess on longer term and also using the data efficiently?
Answered by B33fb0n3
the best is to use as much server components as possible, so you can easily call your fetch functions inside these components. I guess we both know what looks better:
This (server component):
Or this (client component):
This (server component):
// Server component example
export default async function UserPage({ params }) {
const res = await fetch(`https://api.example.com/users/${params.id}`);
const user = await res.json();
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
Or this (client component):
// Client component example with useEffect
import { useEffect, useState } from 'react';
export default function UserPage({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
async function fetchUser() {
const res = await fetch(`https://api.example.com/users/${userId}`);
const data = await res.json();
setUser(data);
}
fetchUser();
}, [userId]);
if (!user) return <p>Loading...</p>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
35 Replies
the best is to use as much server components as possible, so you can easily call your fetch functions inside these components. I guess we both know what looks better:
This (server component):
Or this (client component):
This (server component):
// Server component example
export default async function UserPage({ params }) {
const res = await fetch(`https://api.example.com/users/${params.id}`);
const user = await res.json();
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
Or this (client component):
// Client component example with useEffect
import { useEffect, useState } from 'react';
export default function UserPage({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
async function fetchUser() {
const res = await fetch(`https://api.example.com/users/${userId}`);
const data = await res.json();
setUser(data);
}
fetchUser();
}, [userId]);
if (!user) return <p>Loading...</p>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
Answer
African Slender-snouted CrocodileOP
Have some more questions about first type
1. Loading is going to be handeled by Next itself right? it will not try to render the user.name and user.email before it finish fetching the data? ( Like the unique React without loading statement does 😂 )
2. Is it fine to fetch data inside my components? wouldn't be the best to render all needed data inside the page.tsx component and then pass params through other components?, and inside those components will render data ONLY if needed ( like in the case u provided )
3. What if for example im working on a CMS. and I wanna update something in my DB, and then also see it in "real-time" inside my web app? for example idk.. im managing some users, and I change the email/salary/some other stuff, and I make an API call that changes this data, then I have to re-render the page so the data is being fetched again and loading the right data, how can I get rid of that re-render? or how can I evnetually force re-render so the user doesn't have to reload the page himself? ( mostly reffering to how useEffect dependency works, u know when u fetch some data and u just call that useEffect each time some data changes, so u re-render that part )
4. If i can simply fetch data inside my components like u've said above, inside Page.tsx for example I would use getServerSideProps/getStaticProps ? or when should I use those 2?
1. Loading is going to be handeled by Next itself right? it will not try to render the user.name and user.email before it finish fetching the data? ( Like the unique React without loading statement does 😂 )
2. Is it fine to fetch data inside my components? wouldn't be the best to render all needed data inside the page.tsx component and then pass params through other components?, and inside those components will render data ONLY if needed ( like in the case u provided )
3. What if for example im working on a CMS. and I wanna update something in my DB, and then also see it in "real-time" inside my web app? for example idk.. im managing some users, and I change the email/salary/some other stuff, and I make an API call that changes this data, then I have to re-render the page so the data is being fetched again and loading the right data, how can I get rid of that re-render? or how can I evnetually force re-render so the user doesn't have to reload the page himself? ( mostly reffering to how useEffect dependency works, u know when u fetch some data and u just call that useEffect each time some data changes, so u re-render that part )
4. If i can simply fetch data inside my components like u've said above, inside Page.tsx for example I would use getServerSideProps/getStaticProps ? or when should I use those 2?
1. yea, everything is calculated (and awaited) first and then send to the client
2. yes, that would be the correct way: split the fetching function to the components that need them. A common case is to fetch data in a server component that imports a client component and then to pass the data to the client component (like that it's SSR & load fast without a mess of hooks)
3. you can either use revalidatePath or revalidateTag to rerender only the parts of your page that have been changed or handle the update by your client
4. Nextjs has two different routers: Pages Router (old & complicated) App Router (new & easy). Inside the App router (that I would recommend to use) there are no
2. yes, that would be the correct way: split the fetching function to the components that need them. A common case is to fetch data in a server component that imports a client component and then to pass the data to the client component (like that it's SSR & load fast without a mess of hooks)
3. you can either use revalidatePath or revalidateTag to rerender only the parts of your page that have been changed or handle the update by your client
4. Nextjs has two different routers: Pages Router (old & complicated) App Router (new & easy). Inside the App router (that I would recommend to use) there are no
getServersideProps/getStaticProps
functionsAfrican Slender-snouted CrocodileOP
Okey so with the new App Router there is no need of getServersideProps and getStaticProps, it's straight up fetching wow.
And a last one question, I assume the best way of building my project in term of folders would be like
/lib or /services ( where I make exported functions that lately I use to fetch my data just by awaiting that function inside my main Page.tsx, mostly very useful when u work with services like firebase instead of a restful api where u just have to give a http ), then since next is handling all the loading I assume we dont need hooks anymore to handle loading and error handling, we can easily make it straight up inside the /services file when we make the API/firebase call, then we have Routes around the app ( folders with specific names and page.tsx ), then components for reusable components and maybe some utils inside services or outside of it for some reusable helpful functions??? that sounds too clean and easy to be real 😂 is that for real?
And a last one question, I assume the best way of building my project in term of folders would be like
/lib or /services ( where I make exported functions that lately I use to fetch my data just by awaiting that function inside my main Page.tsx, mostly very useful when u work with services like firebase instead of a restful api where u just have to give a http ), then since next is handling all the loading I assume we dont need hooks anymore to handle loading and error handling, we can easily make it straight up inside the /services file when we make the API/firebase call, then we have Routes around the app ( folders with specific names and page.tsx ), then components for reusable components and maybe some utils inside services or outside of it for some reusable helpful functions??? that sounds too clean and easy to be real 😂 is that for real?
yea, that sounds like a good folder structure. I am using nearly the same 👍
African Slender-snouted CrocodileOP
Can you tell me on short what are you using currently, just so I can make a short idea? So I can improve something in my structure maybe? ^^
Argentine ant
just wanted to jump in here and say if you call revalidateTag, any other components that have revalidated by other means i.e time revalidation or something will also refresh
African Slender-snouted CrocodileOP
Then, is there any way u can re-render only the updated component?
I'm pretty sure it's a common thing to update something and u want it to view it real-time updated on client and not needing a completly refresh or re-render
Argentine ant
as far as i’m aware, you have to use a client component i.e pass initial data from the server component and just refetch that client components data
the issue is revalidateTag (when called in a server action) will just re-render all server components, not just the server components using it
so if those other components have revalidated by other means i.e someone else has called revalidateTag on them or the time revalidation has triggered, it’ll refresh that too
African Slender-snouted CrocodileOP
I mean, this case it's still not that bad, it's defenetly better than having to re-render everything, but yea it's not the cleanest way by far
hmmm, wait how can u do that? u create a Server component, u fetch data, then u pass the props inside the component, and create a useEffect inside the client component that uses as dependency the updatedData and on the useEffect call u refatch the specific data?
Argentine ant
that’s how you’d do it if you want to just refetch that client data without causing everything else to re-render yeah
African Slender-snouted CrocodileOP
That's a good idea but at the same time a hella MESSSY one xD
To have a more clean way of doing that, use a clientside fetching library. It helps you controlling your data clientside
Korat
What if you have 2 rscs that needs to call the same api (same function) ? How does next handles that part
Argentine ant
a way i have considered doing this just with server components...
my suggestion for this is to create a unique identifier (in a cookie) for every unique client. This will form as part of their own unique cache keys. With 'use cache' we can now cache at the component level which is great, so you can tag a component with uniqueIdFromCookie-dashboard-users, and set it to never expire or revalidate. This takes care of our issue where it would revalidate elsewhere i.e. via time based revalidation. (keep in mind that the functions inside of it can still have their own caching mechanisms for data freshness, this is simply a wrapper to be able to control what gets refreshed as a sideEffect of calling revalidateTag)
so when you load into the page, just call revalidateTag(uniqueIdFromCookie-dashboard-users) in a client component, and we essentially have stale-while-revalidate behaviour
so when you load into the page, just call revalidateTag(uniqueIdFromCookie-dashboard-users) in a client component, and we essentially have stale-while-revalidate behaviour
you could create the cookie in some middleware if it doesn't exist for example
think of this cache key that you create as explicitly just for being able to control what refreshes. You can still use your normal caching strategy under the hood for everything else i.e. if you call revalidateTag on a specific tag that's inside of another component, you could revalidate both
i'll try get an example this weekend
this is made more possible now with the fact we can add a cache tag using 'use cache' directly in a server component
Dutch Smoushond
isn't swr a better approach?
SWR is a clientside fetching library and its a better way to leverage SSR instead of fetching everything clientside
It would call the function once and updates the data in both
Dutch Smoushond
i see
African Slender-snouted CrocodileOP
Such as?
you can use for example SWR or react query
African Slender-snouted CrocodileOP
Also, just got into some issues, the company that I work one, wanted a simple single page website, and since I was interested into starting Next.js I've made the app using Next.js, but I started getting into some issues
1. Using router from useRouter and then using router.push, it force a fully reload page not like useNavigate from React this issue forced me to create the whole app within a single page ( Page.tsx ) and using component to form Page.tsx which was not that bad because it was a SIMPLE app, it's still pretty clean and fine. but still, can I useRouter to not fully reload the page like in React useNavigate?
2. And the issue came when I had to connect the app to Facebook pixel and tiktok pixel to retrieve some data from cookie and I had to useEffect to retrieve the cookie, I wonder is there a way to do the following inside Next.js without client ?
1. Using router from useRouter and then using router.push, it force a fully reload page not like useNavigate from React this issue forced me to create the whole app within a single page ( Page.tsx ) and using component to form Page.tsx which was not that bad because it was a SIMPLE app, it's still pretty clean and fine. but still, can I useRouter to not fully reload the page like in React useNavigate?
2. And the issue came when I had to connect the app to Facebook pixel and tiktok pixel to retrieve some data from cookie and I had to useEffect to retrieve the cookie, I wonder is there a way to do the following inside Next.js without client ?
useEffect(() => {
function getTikTokClickId(): string | null {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('ttclid');
}
function getFacebookClickId(): string | undefined {
const urlParams = new URLSearchParams(window.location.search);
const fbclid = urlParams.get('fbclid');
if (fbclid) {
// If fbclid is available, format the fbc parameter
const version = 'fb';
const subdomainIndex = 2;
const creationTime = Math.floor(Date.now() / 1000);
return `${version}.${subdomainIndex}.${creationTime}.${fbclid}`;
}
}
const ttclid = getCookie('ttclid') || getTikTokClickId();
const fbclid = getCookie('_fbc') || getFacebookClickId();
setFbclid(fbclid);
setTtclid(ttclid);
// Setting CampaignData
setDataToBeSent((prevData: any) => ({
...prevData,
campaign: {
fbclid: fbclid || 'No provided fbclid',
ttclid: ttclid || 'No provided ttclid',
provider: searchParams.utm_source || 'No source provided',
name: decodeURIComponent(
searchParams.utm_campaign || 'No campaign name provided'
).replace(/{{|}}/g, ''),
aid: decodeURIComponent(searchParams.aid || 'No aid provided').replace(
/{{|}}/g,
''
),
},
}));
}, []);
that looks like a whole different topic. Is your initial issue (fetching data with nextjs) solved?
African Slender-snouted CrocodileOP
Yes, I will create a separate thread for that then