I have a mutation named deleteSong. I was wondering after the mutation has passed through how can I pass multiple queries into refetchQueries?
this.props
.deleteSong({
variables: { id },
refetchQueries: [{ query: fetchSongs }] //<-- I only know how to pass 1 query
})
.then(() => {})
.catch(err => {
this.setState({ err });
});
The Apollo Client documentation says the following about refetchQueries: A function that allows you to specify which queries you want to refetch after a mutation has occurred. So it should be possible for you to pass either a single or multiple queries to refetchQueries().
Basically it should be:
this.props
.deleteSong({
variables: { id },
refetchQueries: [{ query: FETCH_SONGS }, { query: FETCH_FOLLOWERS }]
})
.then(() => {})
.catch(err => {
this.setState({ err });
});
Whereas FETCH_SONG and FETCH_FOLLOWERS should be defined by graphql-tag. Maybe let others know if this solution works for you.
Related
Right now I have a use case to use two useMutations to create/update database. So the second one is depends on the success of the first one. And also the second mutation needs to be called in a loop, just think about that I have a array and I need loop through the array and apply the second mutation.
After all these mutation finished I have to refetch another api to update caches, because the cache would be impacted by the two mutations.
I am really struggling with how to achieve this.
From another post: Apollo Client - refetchQueries after multiple updates
I can do probably like
const [creatEnrollment] = useMutation(mut1)
const [updateEnrollment] = useMutation(mut2)
const [toFetch, {loading, error, data}] = useLazyQuery(UsersDocument)
await Promise.all([creatEnrollment(), updateEnrollment()])
const result = () => toFetch({
variables: {name: 'i'}
})
but the problem is 1. I need to execute second mutations after the first one; 2, I need to have an array that applied to second mutations one by one.
I also saw
here How can I wait for mutation execution in React Query?
we can use onSuccess
const mutate1 = useMutation((data) => axios.post('/something', { data }))
const mutate2 = useMutation(somethingResult) => axios.put('/somethingElse', { somethingResult })
<button onClick={() => {
mutate1.mutate('data', {
onSuccess: mutate2.mutate
})
}} />
But still 1. how to loop thru mutate2.mutate? and how to fetch after mutate2 finished
do like this????:
<button onClick={() => {
mutate1.mutate('data', {
onSuccess: mutate2.mutate
})
mutate2.mutate('data', {
onSuccess: query
})
}} />
Thank you for helping!!
You can have a function for useMutation and onSuccess the data which use get on success use other mutation
const mutationFuntion = (id) => {
// this is first mutation
return useMutation(
(newTitle) => axios
.patch(`/posts/${id}`, { title: newTitle })
.then(response => response.data),
{
// 💡 response of the mutation is passed to onSuccess
onSuccess: (data) => {
// call the api which will get all the latest update
},
}
)
}
Get the Data of first mutation
const [addTodo, { data, loading, error }] = mutationFuntion(//send data);
This is consecutive mutation I found it from this https://react-query-v3.tanstack.com/guides/mutations#consecutive-mutations doc
useMutation(addTodo, {
onSuccess: (data, error, variables, context) => {
// Will be called 3 times
},
})
['Todo 1', 'Todo 2', 'Todo 3'].forEach((todo) => {
mutate(todo, {
onSuccess: (data, error, variables, context) => {
// Will execute only once, for the last mutation (Todo 3),
// regardless which mutation resolves first
},
})
})
For handle the promise of every mutation call
const mutation = useMutation(addTodo)
try {
const todo = await mutation.mutateAsync(todo)
console.log(todo)
} catch (error) {
console.error(error)
} finally {
console.log('done')
}
Please you need to verify on what kind of object you want to call mutation in loop it array or some thing alse.
I've followed the docs (and some other posts) about RTK optimistic updates but I can't seem to get my case to work. Here is my createReply mutation:
createReply: builder.mutation<IPost, any>({
query({...body}) {
return {
url: PostPath,
method: 'POST',
body: body,
};
},
async onQueryStarted({...body}, {dispatch, queryFulfilled}) {
const patchResult = dispatch(
resourcesApi.util.updateQueryData('getRepliesById', body.parent, (draft) => {
console.log(draft);
Object.assign(draft, body)
})
)
try {
await queryFulfilled
} catch {
console.log("query was fulfilled");
console.log(patchResult);
// patchResult.undo()
}
}
}),
and this is the getRepliesById query that it's referencing:
getRepliesById: builder.query<IPost, string>({
query: (id: string) => `${PostPath}/${id}/replies`,
}),
When I try to use this mutation, the POST works fine, but I'm given this error:
[Immer] Immer only supports setting array indices and the 'length' property.
(The error call stack ends with onQueryStarted).
Where here am I going against Immer rules?
Edit; Here is body:
const postObj = {
content: reply,
author: user._id,
parent: _parentId,
visibility: {scope: 'public'},
media: [],
ats: [],
kind: 'comment',
};
createReply(postObj);
It seems that data is an array and now you are working with it as if it were an object. An array can only have number-indexed properties and your Object.assign seems to add other properties than that?
I am using redux toolkit query for my data fetching API.
The problem is I am having is error handling redux toolkit returns an error property from the query hook. So the error that is returned from the server is an object with nested data and what trying to access it I get a typescript error when trying to access the data from the error object.
below is how I am declaring my mutation hook
const [updateProgram, {
isLoading: isUpdatingProgram,
error: updateProgramError
}] = useUpdateProgramMutation();
below is how I try to access the error in my code.
onSubmit = {
async(values, {
setSubmitting
}) => {
await updateProgram({ ...values,
id: 'programData.data._id'
});
refetch();
if (updateProgramError) {
enqueueSnackbar('Test message', {
variant: 'error'
});
console.log(updateProgramError?.data?.message);
}
setSubmitting(false);
}
}
now the typescript error I am getting is as below.
Not that I able to console.log(updateProgramError) and is the data in the console
Property 'data' does not exist on type 'FetchBaseQueryError | SerializedError'.
Property 'data' does not exist on type 'SerializedError'.
below is updateProgramError when I log is as a whole on the console
{
"status": 500,
"data": {
"status": "error",
"message": "Something went very wrong!"
}
}
below is how I have implemented useUpdateProgramMutation().
import { ndovuAPI } from './ndovuAPI';
export const ndovuProgramsAPI = ndovuAPI.injectEndpoints({
endpoints: builder => ({
getProgramById: builder.query({
query: (id: string) => `/programs/${id}`,
providesTags: ['Programs']
}),
getAllPrograms: builder.query({
query: queryParams => ({ url: `/programs/`, params: queryParams }),
providesTags: ['Programs']
}),
registerProgram: builder.mutation({
query: body => ({
url: '/programs',
method: 'POST',
body
}),
invalidatesTags: ['Programs']
}),
updateProgram: builder.mutation({
query: body => ({ url: `/programs/${body.id}`, method: 'PATCH', body }),
invalidatesTags: ['Programs']
})
}),
overrideExisting: true
});
// Export hooks for usage in functional components
export const { useGetProgramByIdQuery, useGetAllProgramsQuery, useUpdateProgramMutation } = ndovuProgramsAPI;
The code as you have it written will not work the way you want. You can't reference the error from the hook inside of an onSubmit handler like that. What you'll want to do is this:
const onSubmit = async (values) => {
try {
// unwrapping will cause data to resolve, or an error to be thrown, and will narrow the types
await updateProgram({
...values,
id: 'programData.data._id'
}).unwrap();
// refetch(); // you should most likely just use tag invalidation here instead of calling refetch
} catch (error) {
// error is going to be `unknown` so you'll want to use a typeguard like:
if (isMyKnownError(error)) { // add a typeguard like this
enqueueSnackbar('Test message', {
variant: 'error'
});
} else {
// You have some other error, handle it differently?
}
}
}
References:
https://redux-toolkit.js.org/rtk-query/usage/mutations#frequently-used-mutation-hook-return-values
From what I can infer from this TS error, the type of updateProgramError seems like an union of types 'FetchBaseQueryError', 'SerializedError' and possibly other types, which means that TS only assumes that you can safely access this data property after ensuring that data exists in the object.
In other words, TS does not know which of these types updateProgramError will be unless you do some checking to ensure that.
if (updateProgramError) {
enqueueSnackbar('Test message', {
variant: 'error'
});
if ('data' in updateProgramError) console.log(updateProgramError.data.message);
}
I currently have the mutation hook set up as following:
const bookItem = useMutation(CREATE_BOOKING_MUTATION, {
variables: mutationVariables,
refetchQueries: [{ query: getPosts }]
})
The refetchQueries property is set in place, but console.logging bookItem does not give me access to the returned data.
Edit:
#xadm
I wanted to access the retrieved data from the delete mutation as well using the update property as you mentioned, but still had difficulty accessing the returned info.
const onDeleteHandler = useMutation(DELETE_POST, {
update: (proxy, mutationResult) => {
try {
const { deletePost } = mutationResult.data;
const postFeed = proxy.readQuery({
query: GET_POSTS, variables
})
const data = _.omit(postFeed, [deletePost.id])
proxy.writeQuery({ query: GET_POSTS, variables, data })
}
catch(error){
console.log("Cache update error", error)
}
},
})
I have test app where I'm subscribing to a list of todos. But my add new todo mutation also optimistically updates the UI.
The problem is it's now refreshing the entire react view and I have no idea why.
This is the culprit, it happens when I run this mutation:
export default graphql(addItem, {
options: {
fetchPolicy: 'cache-and-network'
},
props: props => ({
onAdd: item =>
props.mutate({
variables: item,
optimisticResponse: {
__typename: 'Mutation',
addItem: { ...item, __typename: 'Item' }
},
update: (proxy, { data: { addItem } }) => {
let data = proxy.readQuery({ query: listItems });
data.listItems.items.push(addItem);
proxy.writeQuery({ query: listItems, data });
}
})
})
})(AddItem);
It turns out that mutating items locally without all the queried fields (in my case "created" is added by the server) causes the entire query mutation to fail but silently. There's a fix on the way: https://github.com/apollographql/apollo-client/issues/3267
Hopefully this helps others starting out!