T
TanStack2y ago
fair-rose

Recommend way to navigate when destroying with TanStack Query

I am using TanStack Query and TanStack Router in my app. I have a list of items, click on one to go to view its details and from that route I can delete the item. After I delete the item I want to go back to the list. Easy peasy. Should I navigate back to the main list in the route component or should I navigate back to the main list in the useMutation?
export const useRemoveTodoMutation = () => {
const navigate = useNavigate();
return useMutation({
mutationFn: (query: RemoveTodoQuery) => {
return API_CLIENT_INSTANCE.removeTodo(query);
},
onSuccess: () => {
navigate({ to: '/todoList' });
queryClient.invalidateQueries();
},
});
};
export const useRemoveTodoMutation = () => {
const navigate = useNavigate();
return useMutation({
mutationFn: (query: RemoveTodoQuery) => {
return API_CLIENT_INSTANCE.removeTodo(query);
},
onSuccess: () => {
navigate({ to: '/todoList' });
queryClient.invalidateQueries();
},
});
};
This useMutation version works without issue but it feels weird to be pulling the useNavigate hook into my Query hook.
3 Replies
xenial-black
xenial-black2y ago
Ideally you would navigate back from the route component, by passing a callback to your custom useMutation hook. But at the end of the day it all comes down to your app's API design. This could make the useRemoveTodoMutation hook more reusable.
// customUseMutationHook
export const useRemoveTodoMutation = ({
onSuccess
}: {
onSuccess?: () => void
}) => {
const navigate = useNavigate();
return useMutation({
mutationFn: (query: RemoveTodoQuery) => {
return API_CLIENT_INSTANCE.removeTodo(query);
},
onSuccess: () => {
queryClient.invalidateQueries();
onSuccess?.();
},
});
};

// route
export const Route = createFileRoute(...)

const Component = () => {
const navigate = Route.useNavigate();

const onDeleteItem = () => {
navigate({ to: '/somewhere' })
}

const deleteMutation = useRemoveTodoMutation({ onSucccess: onDeleteItem })
...
}
// customUseMutationHook
export const useRemoveTodoMutation = ({
onSuccess
}: {
onSuccess?: () => void
}) => {
const navigate = useNavigate();
return useMutation({
mutationFn: (query: RemoveTodoQuery) => {
return API_CLIENT_INSTANCE.removeTodo(query);
},
onSuccess: () => {
queryClient.invalidateQueries();
onSuccess?.();
},
});
};

// route
export const Route = createFileRoute(...)

const Component = () => {
const navigate = Route.useNavigate();

const onDeleteItem = () => {
navigate({ to: '/somewhere' })
}

const deleteMutation = useRemoveTodoMutation({ onSucccess: onDeleteItem })
...
}
sensitive-blue
sensitive-blue2y ago
You can use the onSuccess callback of the mutate function. It has some additional advantages for navigation: https://tkdodo.eu/blog/mastering-mutations-in-react-query#some-callbacks-might-not-fire
Mastering Mutations in React Query
Learn all about the concept of performing side effects on the server with React Query.
fair-rose
fair-roseOP2y ago
Awesome thanks for the swift reply and great tool!

Did you find this page helpful?