I am using Apollo-client to post a mutation to my graphql server. When the mutation is completed, I want to refetch that data. I tried using the refetchQueries argument in the useMutation hook, however I receive this error when I execute the code:
query option is required. You must specify your GraphQL document in
the query option.
This is the line of code that sends the mutation:
const [addUser, { data, loading, error }] =
useMutation(ADD_USER_QUERY, {
refetchQueries:[GET_USERS_QUERY]
});
This is my query (the hardcoded arguments were to see if the issues was due to passing variables):
export const ADD_USER_QUERY = gql`
mutation {
createUser(name: "Albert Einstein", email: "albert#yahoo.ca") {
id
name
}
}
`;
Thank you!
Alright so I figured out what the issue was. I had to pass an object with a "query" key in the refetchQueries array:
const [addUser, { data, loading, error }] =
useMutation(ADD_USER_QUERY, {
refetchQueries: [{ query: GET_USERS_QUERY }],
});
It's weird though because it isn't mentioned in the ApolloDocs. They simply use an array...
// Refetches two queries after mutation completes
const [addTodo, { data, loading, error }] = useMutation(ADD_TODO, {
refetchQueries: [
GET_POST, // DocumentNode object parsed with gql
'GetComments' // Query name
],
});
Does anyone know why that is?
Thanks!
Related
I have a graphQL query which I run in a React component.
const { loading, error, data, refetch }: ExampleQueryHookResult = useExampleQuery(
{
variables: { clientId: graphQlClientId },
},
);
And then get the data by unpacking it
const exampleData = data?.example?.first;
At that point my ID tells me that the type of exampleData is 'any'.
I would have expected to get properly typed data, as GraphQL has all it needs to do so, right?
What am I missing or doing wrong?
I use "#apollo/client": "^3.5.9"
and have an issue when try to update cache
Here is the problem:
I use useMutation hook and inside this hook try to update my cache
const [createUser, { data, loading, error }] = useMutation(addUser, {
update(cache, { data }) {
const newUser = data?.createUser?.user;
const existUsers: any = cache.readQuery({
query: allUsersQuery
});
cache.writeQuery({
query: allUsersQuery,
data: {
allUsers: [...existUsers?.allUsers.edges, newUser]
}
});
}
});
I receive an errors below
Error 1:
react_devtools_backend.js:4061 Missing field 'edges' while writing result {
"__typename": "UserSchema",
"id": "acb4e46f-b80a-42ee-b2d5-c838b63b2c63",
"name": "456"
}
Error 2:
react_devtools_backend.js:4061 Cache data may be lost when replacing the allUsers field of a Query object.
To address this problem (which is not a bug in Apollo Client), define a custom merge function for the Query.allUsers field, so InMemoryCache can safely merge these objects:
existing: {"__typename":"UserConnection","edges":[{"__typename":"UserEdge","node":{"__ref":"CustomUserNode:6a845e81-ae09-46c5-9b5c-5e250efbad71"}},{"__typename":"UserEdge","node":{"__ref":"CustomUserNode:2cff31b1-ed2b-4245-b7d9-268df82b8c4f"}},{"__typename":"UserEdge","node":{"__ref":"CustomUserNode:95869b70-4c7b-42f1-baad-d7c358caa4ff"}},{"__typename":"UserEdge","node":
I try to googled, try to wrap in try and catch blow, but it doesn't work.
I supposed i missing something but i do not what exactly.
Any comments highly appreciated.
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.
Here again for another question which I can't seem to figure out.
I am using Apollo Client (with react) to communicate with my GraphQL server.
I get an error when I try to perform a mutation and I don't know where the issue is coming from because other mutations work fine.
This is the mutation I'm having an issue with:
const DELETE_USER_MUTATION = gql`
mutation deleteUser($id: ID!) {
deleteUser(id: $id) {
id
}
}
`;
The mutation works fine when I input it in the GraphQL playground...
This is how I call the mutation (triggered by a button click):
<button
onClick={() =>
deleteUser({
variables: {
id: user.id,
},
})
}>
Note: user.id is obtained by performing a query and it is valid.
Finally, this is how the deleteUser function is defined (using the useMutation hook)
const [deleteUser, { error }] = useMutation(DELETE_USER_MUTATION, {
refetchQueries: [{ query: GET_USERS_QUERY }],
});
On a side note, how can one debug such errors? On the console I get an error 400, which I read means that it's probably an error with the mutation itself. But I don't get more information about the error... I tried to catch it but to no avail.
Thank you!
I was able to solve the issue and I can't believe I didn't realize the problem sooner.
After checking the type of user.id, I realized it was a string instead of an integer. Converting it to a an Int worked.
However I'm having some cache issues now... Time for more research
I've a bit of confusion how to perform multiple mutation in graphql.
I've read articles about using of graphql() function or compose() function, but i haven't understood if these are the legacy way to do it (version 2)
or is still a valid way to proceed.
I'm currently using apollo 3.3 with grandstack and in my component i do the following to perform a mutation (i used useMutation() hook provided by ApolloClient)
const CreateFBUser = gql `
mutation CreateFBUser($name: String, $fbId: String!) {
CreateFBUser(name: $name, fbId: $fbId) {
_id
id
fbId
name
}
}
`
function FbUserForm() {
const { id } = useParams()
const [formState, setFormState] = useState({})
const [createFBUser,{data: createData}] = useMutation(CreateFBUser)
const { loading, error, data } = useQuery(FBUser,{
variables: {_id: id}
});
...
..
.
as you can see, i havent used components like <Query>, and so on..
FIRST QUESTION: is this component related to the apollo old version? are still regulary used or useMutation() hook is the first choice in apollo 3?
SECOND QUESTION: i need to perform a second mutation related to the first, and i need the generated id from the first mutation to execute the second
//the first mutation
const CreateFBUser = gql `
mutation CreateFBUser($name: String, $fbId: String!) {
CreateFBUser(name: $name, fbId: $fbId) {
_id
id
fbId
name
}
}
`
//the second mutation (pseudocode)
const AddFBUserMemberOf = gql`
mutation AddFBUserMemberOf($from: _GroupInput!, $to: _FBUserInput!) {
AddFBUserMemberOf(from: $from, to: $to) {
from
to
}
}
`
moreover, the second mutation should be performed conditionally according to a value/a variable/something else
The render prop components are deprecated and will not receive further updates or bug fixes according to the docs
For your second question; the mutation function returned from useMutation takes an onCompleted property in the options parameter that executes after the mutation successfully completes.
const [createFBUser,{data: createData}] = useMutation(CreateFBUser)
const [addFBUserMemberOf] = useMutation(AddFBUserMemberOf)
createFBUser({
variables: {
//...
},
onCompleted: (data) => {
// data contains the result of createFBUser
addFBUserMemberOf({
variables: {
//...
}
})
}
})