react-query useQuery not refetching after useMutation - reactjs

I am using react-query (actually tanstack/react-query v4) to query from and mutate a db. Based on docs and research, I gather that useQuery will automatically refetch from the server if/when the server-state differs from the cached state.
However, after I useMutation to update my db, the impacted query does not immediately refetch.
I know that the useMutation is working based on viewing the db on server-side, but also because I can manually refetch using react-query dev tools, and get the new values just fine.
On reading, I have tried two approaches:
the "invalidateQueries" pattern, hoping that the useQuery refetches and re-renders (from the docs on queryInvalidation: "...If the query is currently being rendered via useQuery or related hooks, it will also be refetched in the background")...
const addMover = useMutation({
mutationFn: (newMover) => { ... },
onSuccess: () => {
queryClient.invalidateQueries(["movers"]);
console.log("The mutation is sucessful!");
},
});
---> When this mutation gets run, I do see the 'onSuccess' console.log() coming through, but the query still shows as 'stale' in the dev-tools and does not get re-rendered.
I also tried (in a different place) the "SetQueryData" pattern from the useMutation response, as outlined in the docs...
const handleDelete = useMutation(
{
mutationFn: (wktID) => { ... },
onSuccess: (data) => {
queryClient.setQueryData(["workouts", [activeMover]], data);
},
}
);
My expectation from either approach is simply that the mutated db gets re-queried and re-rendered. I'd prefer to SetQueryData and save a network request, but either approach would make me happy :).

If you want to re-fetch data after mutation you edit your mutation and leave it like this:
const [createHabit, { error, loading }] = useMutation(CREATE_HABIT_MUTATION, {
refetchQueries: [{ query: HABITS_QUERY }],
});
Here you can find an example.

Related

React Query - How to update query results in a React Query with enabled option set?

I'm using React Query for async state management in my React Native app. I've a useQuery() hook with following query options:
useQuery Hook in useStudentAssignments.ts:
const data = useQuery(
studentAssignmentKeys.list({
assignedToIdEq: studentId
}),
async ({ queryKey: [{ params }] }) => {
// Function to persist assignment media
},
{
enabled: !!assignment && !!isConnected,
cacheTime: Infinity,
staleTime: Infinity,
retry: 0,
refetchOnReconnect: 'always',
refetchOnMount: 'always',
onSuccess(data) {
// Async storage of assignments
},
onError() {
// show error message
}
}
App.tsx:
In the main screen I'm using the useQuery hook as:
const { data, isLoading, isSuccess, isError, refetch, isStale } = useStudentAssignments(
studentAssignment?.id
);
useEffect(() => {
if (!!isConnected) {
refetch();
}, [refetch, isConnected);
}
I've these questions:
Since, I've enabled option set, my understanding is that I can't use QueryClient.invalidateQueries(), since it will have no effect. How else can I mark the query key as stale so that it can automatically be refresh?
If automatic query refresh isn't possible, how can I refresh it on some condition or state change?
useQuery() method has an async function (queryFn). Does this function runs only once, or on intervals/ every fetch?
the invalidateQueries, refetchQueries is ignored only when the enabled is false. So when it becomes true, those function could work as your expected
you could call refetch to manually update your data. In RN, it would be called when screen/components is focused.
As I know, it would be triggered when an instance of useQuery is mounted or refetch called. However, they would save the result into cache, then within the cache timeout, it would return the data from cache when you call that from second instance, while queryFn is still running on background. When completed, it updates the cache data.

What is the Syntax for Refetching a Query after a Local State Change in React

I am relatively new to Apollo and GraphQL, and I need to make a requery after several mutations, since the recoil states don't want to update in time and a million errors get thrown off after some of the mutations. I simply put do not know how to do this and have been unable to find any relevant documentation to my scenario.
The following code is inside of theApp.js file.
// Mutations
const { loading: loadingO, error: errorO, data: dataO, refetch: refetchO } = useQuery(GET_OWNER)
const { loading: loadingM, error: errorM, data: dataM, refetch: refetchM } = useQuery(GET_MANAGER)
const handleRefresh = () => {
setRefresh(!refresh)
if (role && id){
if (role == "MANAGER"){
// reftechM
}
if (role == "OWNER"){
// refetchO
}
}
}
useEffect( () => {
console.log("???")
}, [refetchM, refetchO])
...where handleRefresh is essentially called after every mutation, or of course, every refresh. However, just calling refetch does not work, and I have been very unable to find the proper syntax for my issue. Does anyone know the solution?
By default, the useQuery hook checks the Apollo Client cache to see if all the data you requested is already available locally. If all data is available locally, useQuery returns that data and doesn't query your GraphQL server. This cache-first policy is Apollo Client's default fetch policy. If you say that you will call handleRefresh() after mutation the below code will work fine.
here read fetch policy
const { loading: loadingO, error: errorO, data: dataO, refetch: refetchO } = useQuery(GET_OWNER, {
fetchPolicy: "network-only",
})
const { loading: loadingM, error: errorM, data: dataM, refetch: refetchM } = useQuery(GET_MANAGER, {
fetchPolicy: "network-only",
})
const handleRefresh = () => {
setRefresh(!refresh)
if (role && id){
if (role == "MANAGER"){
refetchM()
}
if (role == "OWNER"){
refetchO()
}
}
}
Try this directly from apollo docs:
Refetching queries after a mutation
In certain cases, writing an update function to update the cache after
a mutation can be complex, or even impossible if the mutation doesn't
return modified fields.
In these cases, you can provide a refetchQueries option to the
useMutation hook to automatically rerun certain queries after the
mutation completes.
For details, see Refetching queries.
Note that although refetchQueries can be faster to implement than an
update function, it also requires additional network requests that are
usually undesirable. For more information, see this blog post."
Source: https://www.apollographql.com/docs/react/caching/advanced-topics/

Why is queryClient.setQueryData taking so long in this example with react-query?

I'm updating cache with react-query using useQuery and setQueryData, the problem is setQueryData can take up to 2mins to update the data, possibly due to a loop of some sort. I'm mapping each page on the 'Styles' data, and updating the Styles, Groups and Ranges on the page that matches the pageIndex with data from the response. I have no idea why the very long update, an I using react-query wrong here? The data being updated is not huge at all by the way.
Thanks in advance.
export const useStyle = (styleId, pageIndex) => {
const queryClient = useQueryClient();
const { refetch } = useQuery('Style', () => fetchStyle(styleId), {
staleTime: Infinity,
refetchOnWindowFocus: false,
initialData: {},
onSuccess: (res) => {
queryClient.setQueryData('Styles', (oldData) => ({
pages: map(oldData.pages, (page, index) => ({
...page,
...(index === pageIndex
? {
Styles: {
...page.Styles,
...res.Styles,
},
Groups: {
...page.Groups,
...res.Groups,
},
Ranges: {
...page.Ranges,
...res.Ranges,
},
}
: {}),
})),
pagesParams: oldData.pageParams,
}));
},
});
return { refetchStyle: refetch };
};
I have solved this problem by using useMutation instead of useQuery and setQueryData, this is the better approach if a loop of onSuccess'es is caused by setQueryData.
I think you're creating an infinite loop here:
useQuery('Styles') subscribes to the key Styles, so this component will always update / re-render whenever something in the cache under that key changes
in onSuccess of this query, you update that very same key (Styles)
this update informs all observers, because they should be aware of that change
calling setQueryData also triggers onSuccess, because setQueryData is to be treated the same as if the data came from the backend via the queryFn.
this will trigger your onSuccess again and so on...
The better question would be: What problem are you trying to solve? By the looks of it, you want to perform some data transformation after each fetch. For this, you have multiple options, all of which I have outlined extensively here: https://tkdodo.eu/blog/react-query-data-transformations
But usually, merging data like that should not be necessary. The cache should best be a 1:1 mapping of the response you get from the server.

Why react useQuery() doesnt fetch the newest data after a mutation?

I have a code like this
const [inputComment, setInputComment] = useState('');
const [
commentPost,
{ data: data4, loading: loading4, errorCreate4 },
] = useMutation(COMMENT_POST);
const { error: error2, loading: loading2, data: data2 } = useQuery(
GET_POST_BY_ID,
{
variables: {
postid: item.id,
},
},
);
const doComment = () => {
commentPost({
variables: {
postId: item.id,
userEmail: email,
comment: inputComment,
},
})
.then(({ data }) => {
setInputComment('');
console.log('success');
})
.catch((e) => {
console.log('not success');
});
};
This is supposed to get the data, and when I do comment then it runs the mutation and re-render everything.
My problem is, it re-render alright BUT the data that the useQuery fetch is not the newest data a.k.a the data before I add a new comment.
Does anyone know how to fix this problem??
Please help :(
Your mutation modifies data on the server side.
Once your mutation is done, you should refetch your data in order to get the modified version in your local cache on the client side.
By guessing how your mutation and query actually work, here is how it would look like:
const [
commentPost,
{ data: data4, loading: loading4, errorCreate4 },
] = useMutation(COMMENT_POST, {
refetchQueries: [
{ query: GET_POST_BY_ID, variables: { postid: item.id } }
]
});
Otherwise, intead of refetching from the server, you could update the local cache directly.
More info can be found here in the official documentation.
I assume commentPost is an insert operation, not an update of a single record. In this case, Apollo useMutation will not update the cache for you. You need to modify the cache yourself. The official Apollo documentation has covered this use case with an example. You may want to revise the usage of writeFragment as well.
Below are directly from apollo docs on cache update for list fields.
In most cases, a mutation response should include any object(s) the
mutation modified. This enables Apollo Client to normalize those
objects and cache them according to their __typename and id fields (by
default).
...
When a mutation's response is insufficient to update all modified
fields in your cache (such as certain list fields), you can define an
update function to apply manual changes to your cached data after a
mutation.
const [addTodo] = useMutation(ADD_TODO, {
update(cache, { data: { addTodo } }) {
cache.modify({
fields: {
todos(existingTodos = []) {
const newTodoRef = cache.writeFragment({
data: addTodo,
fragment: gql`
fragment NewTodo on Todo {
id
type
}
`
});
return [...existingTodos, newTodoRef];
}
}
});
}
});
EDIT
I noticed another answer suggests using refetch, which is not a bad option for starters. However, updating the cache is the recommended approach over refetch. You can refer to the Apollo blog article When To Use Refetch Queries in Apollo Client.
Below are some quotes you should note from this article.
If you’re just getting started with GraphQL, I think the mental model of passing in the queries that you’d like to re-run after a mutation is an easy one to wrap your head around.
...
The advantage here is that this approach is straightforward. The disadvantage is that we’re fetching the entire list of data again when we might not need to.
...
For a more efficient use of bandwidth and network round-trips, we can rely on cache normalization and update functions.

How to get refetchQueries data in reactjs 16.8

I'm trying to use refetchQueries to get an updated list of record after a mutation. I have tried below code to get updated data but the updated data showing in network tab of the developer console.
This is for reactjs 16.8 and "react-apollo": "^2.5.5" and it seems awaitRefetchQueries: true not working
client.mutate({
mutation: ADD_ACTIVE_CREW,
variables: {
activeCrewInfo: {
employee: activeCrew[0].vp_id,
date_on_crew: new Date(),
crew_id: selectedCrewId
}
},
options: () => ({
refetchQueries: [
{
query: GET_EMPLOYEE_BY_CREW_ID_QUERY,
variables: { crew_id: crew_id }
}
]
}),
awaitRefetchQueries: true
});
I'm getting correct response in developer console's network tab, but not able to access in my reactjs application.
The mutation data itself will never reflect the refetched queries.
Any queries you tell Apollo to refetch will be requested and then have their result written to the cache. That means any Query components or components using useQuery or watchQuery will be updated, provided the query and variables parameters match what's in refetchQueries.
If you don't already have a component that uses that query, you need to add one.

Resources