T
TanStack•3y ago
xenial-black

useQueryClient inside a custom hook

Hi, I'm pretty new to React Query. I've done a pretty solid scan of docs, StackOverflow and the archive of QA here. I've got a CRUD app with 'parent' queries (arrays of a given data type) and 'child' queries (a single instance of a data type). When mutating a child, I want both of the corresponding queries to update using setQueryData . To encapsulate this behaviour, I've written a custom hook, that can be called from within a component (ie when an edit form is submitted). Custom hook:
const { useMutation, useQueryClient } = require("@tanstack/react-query");

function useMutateAndUpdate({parentQueryKey, childQueryKey, mutateFunction, isNew = false, onSuccess}) {

const queryClient = useQueryClient();

const mutation = useMutation({
mutationFn: (data) => mutateFunction(data),
onSuccess: ({screen: data}) => {
// do some stuff to setQueryData in parent and child queries
onSuccess(data);
}
})
return mutation;
}

export {useMutateAndUpdate}
const { useMutation, useQueryClient } = require("@tanstack/react-query");

function useMutateAndUpdate({parentQueryKey, childQueryKey, mutateFunction, isNew = false, onSuccess}) {

const queryClient = useQueryClient();

const mutation = useMutation({
mutationFn: (data) => mutateFunction(data),
onSuccess: ({screen: data}) => {
// do some stuff to setQueryData in parent and child queries
onSuccess(data);
}
})
return mutation;
}

export {useMutateAndUpdate}
Hook usage I then call it from within my component with
const saveOrUpdate = useMutateAndUpdate({
parentQueryKey: ['screens'],
childQueryKey: ['screens', {id: screen.id || 'new'}],
mutateFunction: screenActions.saveOrUpdate,
onSuccess: (data) => navigate(`/screens/${data.id}`),
isNew: _.isEmpty(screen.id)
});
const saveOrUpdate = useMutateAndUpdate({
parentQueryKey: ['screens'],
childQueryKey: ['screens', {id: screen.id || 'new'}],
mutateFunction: screenActions.saveOrUpdate,
onSuccess: (data) => navigate(`/screens/${data.id}`),
isNew: _.isEmpty(screen.id)
});
I'd like to reuse this for various data types, and cover off the 'create a new' as well as the 'update an existing' use case. The custom hook throws an error that No QueryClient set, use QueryClientProvider to set one This isn't the case, there is definitely a QueryClientProvider wrapping my app. I'm not sure why it would be inaccessible inside the hook. I can work around this by passing the result of useQueryClient into the custom hook, but is that the only way this can work? Other hooks like useState can be used in a custom hook, so I'm not sure what I'm doing wrong here.
5 Replies
extended-salmon
extended-salmon•3y ago
Hi. It definitely has nothing to do with a custom hook. Do you use NextJs or smth?
xenial-black
xenial-blackOP•3y ago
No, this is just plain React, compiled with Webpack. I refactored it to pass the queryClient in, and now it returns the same error, but on the useMutation call, so I'm obviously not structuring my hook code correctly. Any example of a custom hook wrapping a react query hook that I can compare to? https://tanstack.com/query/v4/docs/react/guides/updates-from-mutation-responses has an example of something very similar
extended-salmon
extended-salmon•3y ago
export function useActivateTeamMember() {
const queryClient = useQueryClient();

return useMutation({
mutationFn: services.users.activateTeamMember,
onSuccess: (data, uuid) => {
queryClient.invalidateQueries(['team-members']);
queryClient.invalidateQueries(['team-member', uuid]);
},
});
}
export function useActivateTeamMember() {
const queryClient = useQueryClient();

return useMutation({
mutationFn: services.users.activateTeamMember,
onSuccess: (data, uuid) => {
queryClient.invalidateQueries(['team-members']);
queryClient.invalidateQueries(['team-member', uuid]);
},
});
}
export function useTeamMember(uuid?: string) {
return useQuery({
queryKey: ['team-member', uuid],
queryFn: () =>
services.users.getTeamMembersByID(uuid).then((res) => res.data),
enabled: !!uuid,
});
}
export function useTeamMember(uuid?: string) {
return useQuery({
queryKey: ['team-member', uuid],
queryFn: () =>
services.users.getTeamMembersByID(uuid).then((res) => res.data),
enabled: !!uuid,
});
}
Can you show your component where you use those hooks and its parent? Maybe the problem is somewhere up in the components tree?
extended-salmon
extended-salmon•3y ago
Stack Overflow
"Error: No QueryClient set, use QueryClientProvider to set one"
I was trying React Query and got this at the start of my React app. import React from 'react' import { useQuery } from "react-query";
const fetchPanets = async () => { const ...
xenial-black
xenial-blackOP•3y ago
Thanks so much for the example, I rebuilt mine successfully around it, and then re-compared them. The answer is on the first line, twas a dumb auto-complete in my IDE - it decided to 'require' the tanstack modules... not execute an import 🙄 . Surprised it compiled happily...

Did you find this page helpful?