T
TanStack•9mo ago
flat-fuchsia

deserialized data is correct, stale data still shows. how do you properly overwrite cache from RSC?

I have a pretty straightforward setup here. In a server component I have
import { api, HydrateClient } from "~/trpc/server";

export default async function Posts() {
await api.posts.getPosts.prefetch();
return (
<HydrateClient>
<ClientComponent />
</HydrateClient>
);
}
import { api, HydrateClient } from "~/trpc/server";

export default async function Posts() {
await api.posts.getPosts.prefetch();
return (
<HydrateClient>
<ClientComponent />
</HydrateClient>
);
}
In the client component:
import { api } from "~/trpc/react";

export function ClientComponent() {
const [data] = api.posts.getPosts.useSuspenseQuery();

return ...
}
import { api } from "~/trpc/react";

export function ClientComponent() {
const [data] = api.posts.getPosts.useSuspenseQuery();

return ...
}
Now in this client component you can click a button to edit a post, which takes you to a simple edit post page. The hook I use for editing the post looks like this:
export function useEditPost() {
const router = useRouter();
const utils = api.useUtils();

return api.posts.editPost.useMutation({
onMutate: () => {
utils.posts.getPosts.cancel();
},
onSuccess: async () => {
toast.success("Post updated");
router.push("/posts");
},
});
}
export function useEditPost() {
const router = useRouter();
const utils = api.useUtils();

return api.posts.editPost.useMutation({
onMutate: () => {
utils.posts.getPosts.cancel();
},
onSuccess: async () => {
toast.success("Post updated");
router.push("/posts");
},
});
}
No here's where I'm having difficulty. The onSuccess routes the user back to the posts page. The posts page hits the RSC, which correctly gets the freshest data (with the edited posts details), but for a brief moment the ClientComponent still shows the old values. Simply put, I want values from the RSC to always override any previously cached values. My workaround for now is to call removeQueries on getPosts, which feels okay since I'm removing the query right before heading to the RSC which will hydrate it, but I'm not sure this is the right pattern - @TkDodo 🔮 seems to encourage not using removeQueries. Any ideas on how to move forward here? Am I missing something?
1 Reply
flat-fuchsia
flat-fuchsiaOP•9mo ago
One approach is to go the initialData route and pass in the initialData from the RSC, then in the client component do
const { data } = api.posts.getPosts.useQuery(undefined, {
gcTime: 0,
initialData,
});
const { data } = api.posts.getPosts.useQuery(undefined, {
gcTime: 0,
initialData,
});
but I'd love to use the prefetching pattern if possible / really understand what it is I'm seeing here and how others deal with the issue. Simply put, I want data fetched in an RSC to always replace existing cache data, / I'm unsure why this doesn't happen by default? Genuinely curious here - am I doing something special / out of the ordinary? Okay after doing more research, I think this is where I'm at. I believe this is happening (and to be expected) because of the new hydration changes in react query v5 now happening in an effect @TkDodo 🔮 would love your take on this: I think these new hydration changes in v5 expose a valid use case for removeQueries IMO. Since hydration changes now occur in an effect, if I redirect a user to a route that has an RSC entry point which prefetches data, and I know that query might be cached and want to avoid a flicker of stale data (bc of hydration in a useEffect) removing the potentially cached query with removeQueries before navigating knowing the RSC will prefetch and supply seems like an elegant solution. Do you disagree?

Did you find this page helpful?