T
TanStack5mo ago
national-gold

How to change pendingComponent behavior?

Hi everyone, I'm working with the TanStack Router and trying to utilize the pendingComponent feature. I want it to display only during the initial page load when navigating to a new route, but currently, it shows up every time a query within the loaded component is triggered. Does anyone know how to restrict the pendingComponent so that it only shows during route transitions and not during subsequent query calls in the component? Any guidance or examples would be greatly appreciated! Thanks in advance!
9 Replies
conscious-sapphire
conscious-sapphire5mo ago
it depends on how you load the data a complete minimal example would help
national-gold
national-goldOP5mo ago
Here's an example:
import { createFileRoute } from '@tanstack/react-router';
import { Spinner } from '@/shared/components/loaders/Spinner';
import { Modal } from '@/shared/components/dialog/Modal';
import { useSuspenseQuery } from '@tanstack/react-query';
import { Suspense, useState } from 'react';

export const Route = createFileRoute('/org/$orgId/project/$projectId/items/')({
component: () => <ProjectList />,
pendingComponent: () => <Spinner label="Loading items..." />,
});

function ProjectList() {
const [isModalOpen, setModalOpen] = useState(false);
const { data: items } = useSuspenseQuery(['items'], fetchItems);

return (
<div>
<button onClick={() => setModalOpen(true)}>Edit Item</button>
<Suspense fallback={<Spinner label="Loading table..." />}>
<div>{items.map((item) => item.name).join(', ')}</div>
</Suspense>
<Modal isOpen={isModalOpen} setIsOpen={setModalOpen} title="Edit Item">
<Suspense fallback={<Spinner label="Loading form..." />}>
<ItemEditForm itemId="123" />
</Suspense>
</Modal>
</div>
);
}

function ItemEditForm({ itemId }) {
const { data: item } = useSuspenseQuery(['item', itemId], () =>
fetchItemById(itemId)
);

const onSubmit = () => {
console.log('Form submitted');
};

return (
<form onSubmit={onSubmit}>
<input defaultValue={item.name} placeholder="Name" />
<textarea defaultValue={item.description} placeholder="Description" />
<button type="submit">Save</button>
</form>
);
}

// Mock API
const fetchItems = async () => [{ name: 'Item 1' }, { name: 'Item 2' }];
const fetchItemById = async (id) => ({ name: `Item ${id}`, description: '' });
import { createFileRoute } from '@tanstack/react-router';
import { Spinner } from '@/shared/components/loaders/Spinner';
import { Modal } from '@/shared/components/dialog/Modal';
import { useSuspenseQuery } from '@tanstack/react-query';
import { Suspense, useState } from 'react';

export const Route = createFileRoute('/org/$orgId/project/$projectId/items/')({
component: () => <ProjectList />,
pendingComponent: () => <Spinner label="Loading items..." />,
});

function ProjectList() {
const [isModalOpen, setModalOpen] = useState(false);
const { data: items } = useSuspenseQuery(['items'], fetchItems);

return (
<div>
<button onClick={() => setModalOpen(true)}>Edit Item</button>
<Suspense fallback={<Spinner label="Loading table..." />}>
<div>{items.map((item) => item.name).join(', ')}</div>
</Suspense>
<Modal isOpen={isModalOpen} setIsOpen={setModalOpen} title="Edit Item">
<Suspense fallback={<Spinner label="Loading form..." />}>
<ItemEditForm itemId="123" />
</Suspense>
</Modal>
</div>
);
}

function ItemEditForm({ itemId }) {
const { data: item } = useSuspenseQuery(['item', itemId], () =>
fetchItemById(itemId)
);

const onSubmit = () => {
console.log('Form submitted');
};

return (
<form onSubmit={onSubmit}>
<input defaultValue={item.name} placeholder="Name" />
<textarea defaultValue={item.description} placeholder="Description" />
<button type="submit">Save</button>
</form>
);
}

// Mock API
const fetchItems = async () => [{ name: 'Item 1' }, { name: 'Item 2' }];
const fetchItemById = async (id) => ({ name: `Item ${id}`, description: '' });
Every time the modal is opened, the query in the modal will trigger the pending component.
conscious-sapphire
conscious-sapphire5mo ago
national-gold
national-goldOP5mo ago
So it looks like my issue could be solved with useDeferredValue hook. I'm not sure where in the example where I would use this hook. The example in the linked article used the hook with search parameters. I thought it would have something to do with the modal state, but it appears to be some other state change causing the page to suspend. I have to update the example that could be relevant here
conscious-sapphire
conscious-sapphire5mo ago
if you can add a complete minimal example (i.e. runnable, e.g. by forking one of the existing router example on stackblitz), we could have a look
national-gold
national-goldOP5mo ago
Alright, I forked one of the examples and changed some of the code, but it reproduces the exact issue I have. Here is the code: https://stackblitz.com/edit/tanstack-router-vmdpdgth?file=src%2Froutes%2F__root.tsx It will do the loading animation on the initial page load and then do loading animations whenever you click on the edit button for any of the posts.
Harrison Kaiser
StackBlitz
Router Basic React Query File Based Example (forked) - StackBlitz
Run official live example code for Router Basic React Query File Based, created by Tanstack on StackBlitz
national-gold
national-goldOP5mo ago
Btw, you may need to navigate to the "/" route by typing in that path in the url I tired to setup redirect, but it wasnt working for some reason
conscious-sapphire
conscious-sapphire5mo ago
your PostForm will cause a Suspense in the modal so if you dont want the pendingcomponent to be shown in this case, you need to prevent the suspense to bubble up like this
<Modal
isOpen={isEditPostOpen}
setIsOpen={setIsEditPostOpen}
title="Edit Post"
>
<Suspense>
<PostForm postId={selectedPost?.id || ''} />
</Suspense>
</Modal>
<Modal
isOpen={isEditPostOpen}
setIsOpen={setIsEditPostOpen}
title="Edit Post"
>
<Suspense>
<PostForm postId={selectedPost?.id || ''} />
</Suspense>
</Modal>
national-gold
national-goldOP5mo ago
Huh, interesting. I thought I tried this in my other codebase (not in the example). Let me try something. Ok that worked! I was surrounding the entire modal with suspense Thank you so much for taking a look at this, appreciate it

Did you find this page helpful?