T
TanStack9mo ago
probable-pink

Error: Maximum update depth exceeded while using Route.useLoaderData()

I have this server function. loader: async () => { return { project: getProjectFn(), }; } Then I get error once I use const { project } = Route.useLoaderData(); in the component. Should I not use server function in loader?
8 Replies
probable-pink
probable-pinkOP9mo ago
GitHub
GitHub - webmasterdevlin/tanstack-start-problems
Contribute to webmasterdevlin/tanstack-start-problems development by creating an account on GitHub.
probable-pink
probable-pinkOP9mo ago
Switch this to see the problem.
// const { project } = Route.useLoaderData();
const project = getProjectFn();
// const { project } = Route.useLoaderData();
const project = getProjectFn();
xenial-black
xenial-black9mo ago
Neither technically should work (I believe). Server functions can only return serializable info and Date is not. So return Date().toISOString() in the server function and then create your date object on the front end for display from the ISO String response. https://tanstack.com/router/latest/docs/framework/react/start/server-functions#returning-primitives-and-json
Server Functions | TanStack Router React Docs
What are Server Functions? Server functions allow you to specify logic that can be invoked almost anywhere (even the client), but run only on the server. In fact, they are not so different from an API...
xenial-black
xenial-black9mo ago
Example Component:
const rootRoute = getRouteApi("__root__");

export default function ProjectInfo() {
const { project } = rootRoute.useLoaderData();

return <Await promise={project} fallback={<ProjectInfoSkeleton />}>
{project => {
const startDate = new Date(project.startDate);
const expectedLaunchDate = new Date(project.expectedLaunchDate)

return <div className="flex gap-16">
<div className="flex flex-col gap-2">
<span className="font-bold">About the project</span>
<Info label="Project name:" value={project.name}/>
<Info label="Company:" value={project.companyName}/>
<Info label="Duration:" value={`${startDate.getFullYear()}-${expectedLaunchDate.getFullYear()}`}/>
<Info label="Expected launch:" value={expectedLaunchDate.toLocaleDateString()}/>
</div>
<div className="hidden flex-col gap-2 sm:flex">
<span className="font-bold">Team members</span>
{Object.entries(project.teamMembers).map(([role, member]) => {
return <Chip key={role}>{`${role.split('-').join(' ')} (${member.count})`}</Chip>;
})}
</div>
<div className="hidden flex-col gap-2 md:flex">
<span className="font-bold">Deliverables</span>
{project.deliverables.split(';').map(deliverable => {
return (
<span key={deliverable}
className="w-fit bg-black px-2 py-1 text-white dark:bg-white dark:text-black">{deliverable}
</span>
);
})}
</div>
</div>
}}
</Await>;
}
const rootRoute = getRouteApi("__root__");

export default function ProjectInfo() {
const { project } = rootRoute.useLoaderData();

return <Await promise={project} fallback={<ProjectInfoSkeleton />}>
{project => {
const startDate = new Date(project.startDate);
const expectedLaunchDate = new Date(project.expectedLaunchDate)

return <div className="flex gap-16">
<div className="flex flex-col gap-2">
<span className="font-bold">About the project</span>
<Info label="Project name:" value={project.name}/>
<Info label="Company:" value={project.companyName}/>
<Info label="Duration:" value={`${startDate.getFullYear()}-${expectedLaunchDate.getFullYear()}`}/>
<Info label="Expected launch:" value={expectedLaunchDate.toLocaleDateString()}/>
</div>
<div className="hidden flex-col gap-2 sm:flex">
<span className="font-bold">Team members</span>
{Object.entries(project.teamMembers).map(([role, member]) => {
return <Chip key={role}>{`${role.split('-').join(' ')} (${member.count})`}</Chip>;
})}
</div>
<div className="hidden flex-col gap-2 md:flex">
<span className="font-bold">Deliverables</span>
{project.deliverables.split(';').map(deliverable => {
return (
<span key={deliverable}
className="w-fit bg-black px-2 py-1 text-white dark:bg-white dark:text-black">{deliverable}
</span>
);
})}
</div>
</div>
}}
</Await>;
}
And serverFn:
export const getProjectFn = createServerFn().handler(() => {
return {
id: "1",
name: "Real estate",
companyName: "ACME Inc.",
startDate: new Date().toISOString(),
deliverables: "website",
expectedLaunchDate: new Date().toISOString(),
teamMembers: {
role: {
count: 1,
members: [
{
id: "1",
projectId: "1",
role: "developer",
},
],
},
},
};
});
export const getProjectFn = createServerFn().handler(() => {
return {
id: "1",
name: "Real estate",
companyName: "ACME Inc.",
startDate: new Date().toISOString(),
deliverables: "website",
expectedLaunchDate: new Date().toISOString(),
teamMembers: {
role: {
count: 1,
members: [
{
id: "1",
projectId: "1",
role: "developer",
},
],
},
},
};
});
adverse-sapphire
adverse-sapphire9mo ago
no, I'm pretty sure TSS includes special support for Date serialisation worth trying anyway but I'm pretty sure that's not it
probable-pink
probable-pinkOP9mo ago
I added the toISOString(), still does not work. It used to be Prisma ORM. I hardcoded the value now so anyone can try it without using Prisma. Even just returng an ID wont work.
export const getProjectFn = createServerFn().handler(() => {
return Promise.resolve({
id: "1",
});
});
export const getProjectFn = createServerFn().handler(() => {
return Promise.resolve({
id: "1",
});
});
xenial-black
xenial-black9mo ago
Did you copy paste it as is? I just tried my changes locally with your code and it worked. Could be, I just know Date isn't serializable by default. And their example in the docs showed them using the toISOString method as well
probable-pink
probable-pinkOP9mo ago
It is working in this code for getting data. But not if you switch from getProjectFn to Route.useLoaderData();
// ProjectInfo.tsx
// const { project } = Route.useLoaderData();
const project = getProjectFn();
// ProjectInfo.tsx
// const { project } = Route.useLoaderData();
const project = getProjectFn();
Anyone here is also getting this error? I edited my previous response here. I guess putting the getProjectFn in the loader of my __root won't really work. I moved it to const project = useServerFn(getProjectFn); instead

Did you find this page helpful?