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

Viewed 2513

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 :(

2 Answers

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.

Related