revalidating dynamic routes
Answered
Alapaha Blue Blood Bulldog posted this in #help-forum
Alapaha Blue Blood BulldogOP
I have a client component in which I show a list of items.
This is shown in a dynamic route (as you can see from the example below)
the (client component)
Finally, the
By doing this (especially the revalidatePath at the end of the server action), I would expect the UI to match this change immediately, but the changes apply only after reloading the page.
I tried adding a router.refresh() at the end of the async function in the form
I also noticed that this bug is present only on dynamic routes: I have basically the same logic in the static route
In the
This is shown in a dynamic route (as you can see from the example below)
// app/lists/[listId]
"use client"
export default function ListPage({
params,
}: {
params: { listId: string };
}) {
const { data, isLoading, isError } = api.item.getAll.useQuery({ listId });
// ..loading and error handling..
return <div>
<AddItem/>
{data.map((item) => <ItemCard key={item.id} item={item} />}
</div>
}
the (client component)
AddItem
has the purpose of adding a new item by calling a server action when submitted:"use client"
export const AddItem: React.FC<AddItemProps> = ({ listId }) => {
return <form
action={async () => {
await createItem({
name: "dummyName",
description:"dummyDescription",
listId,
});
}}
>...</form>
}
Finally, the
createItem
server action is as follow:"use server"
export async function createItem(props: ItemCreateInput) {
await api.item.create.mutate(props);
revalidatePath(`/lists/${props.listId}`);
}
By doing this (especially the revalidatePath at the end of the server action), I would expect the UI to match this change immediately, but the changes apply only after reloading the page.
I tried adding a router.refresh() at the end of the async function in the form
action
prop, but that doesn't change anything.I also noticed that this bug is present only on dynamic routes: I have basically the same logic in the static route
app/lists
(where I allow to add a new new list instead), and the UI updates as expected there.In the
revalidatePath
docs page, they mention that "with a dynamic route segment [...] The invalidation only happens when the path is next visited", but they don't provide a way to address this scenario in other ways. What should I do here?Answered by Ray
"use client"
export const AddItem: React.FC<AddItemProps> = ({ listId }) => {
const utils = api.useUtils();
return <form
action={async () => {
await createItem({
name: "dummyName",
description:"dummyDescription",
listId,
});
await utils.item.getAll.invalidate({ listId })
}}
>...</form>
}
13 Replies
@Alapaha Blue Blood Bulldog client page might be an issue, are you sure you need "use client"
wait no its cause you are using an external function aka api.item.getAll
wrap it in a unstable_cache, set the tags and the key parts as the listId
Alapaha Blue Blood BulldogOP
@Arinji thanks for the answer!
Since I'm using trpc, I could actually have that same query in its "server version" (aka without using a hook), but I'm also using some useState in that page (that I haven't included in the original question for simplicity), so it's not clear to me how can I handle this cache invalidation while keeping this as a client component (because those state are needed).
This is the component with the missing states I'm talking about:
Since I'm using trpc, I could actually have that same query in its "server version" (aka without using a hook), but I'm also using some useState in that page (that I haven't included in the original question for simplicity), so it's not clear to me how can I handle this cache invalidation while keeping this as a client component (because those state are needed).
This is the component with the missing states I'm talking about:
// app/lists/[listId]
"use client"
export default function ListPage({
params,
}: {
params: { listId: string };
}) {
const [view, setView] = useState<"card" | "table">("card");
const [sorting, setSorting] = useState<SortingState>([
{
id: "createdAt",
desc: true,
},
]);
const { data, isLoading, isError } = api.item.getAll.useQuery({ listId, options: { sorting }, });
// ..loading and error handling..
return <div>
<AddItem/>
{view == "card" ?
data.map((item) => <ItemCard key={item.id} item={item} />
: <ItemTable
data={data}
sorting={sorting}
setSorting={setSorting}
/>
}
</div>
}
did you try the unstable cache?
it wraps your api
I think you need to invalidate the cache for the query since you are using client side rendering with react-query
or use the mutation from trpc
"use client"
export const AddItem: React.FC<AddItemProps> = ({ listId }) => {
const utils = api.useUtils();
return <form
action={async () => {
await createItem({
name: "dummyName",
description:"dummyDescription",
listId,
});
await utils.item.getAll.invalidate({ listId })
}}
>...</form>
}
Answer
Alapaha Blue Blood BulldogOP
@Ray thank you so much, didn't know about the useUtils, working as expected now 🙌ðŸ¼
and you may not need
revalidatePath
there since you are rendering on client side