We have learned how to POST and GET data through react-query hooks from your server with AXIOS. All of that was tidy and clean work since react query was doing a lot of hard work for you. In this article, we are going to add another change to our reactjs application to believe it or not make it even better and faster. So without further ado let’s go 🚦

What Was Wrong?

Were we doing anything wrong in our code and the answer is ABSOLUTELY NOT? But was it the best solution in terms of performance? No!

Once we posted our new planet to our server through useMutation hook we were invalidating our query against the query key which made it refetch the data.

const successCb = () => {
  queryClient.invalidateQueries(["get-planets"]);
};

We were making two requests

  • POST our new data
  • GET the updated data

What Is The Better Way?

To save our second API we are going to use the onMutate callback to optimize our performance. The idea is to basically

  • Access the cache of react-query
  • Get data we are trying to refetch using its query-key.
  • Manually add our new item to the data
  • And put our updated cache back in its place.

Optimistically Updating Data

We call it optimistically updating our data because we are updating our existing data in our cache without knowing that if our API which is changing the data is actually returning success or false.

onMutate: async (variables) => {
  const { successCb, errorCb } = variables;

  // cancel queries against this query key
  // so that if any other component is consuming this data
  // is not able to get the old data
  await queryClient.cancelQueries(["get-planets"]);

  // get the cached values of 'get-planets'
  const previousValue = queryClient.getQueryData(["get-planets"]);

  // set the cached data with an added object
  // i.e the new planet posted
  queryClient.setQueryData(
    ["get-planets"],
    [...previousValue, { name: variables?.name, id: variables?.id }]
  );

  // return previousValue here 
  // we will use it in the next section
  return { successCb, errorCb, previousValue };
},

Once we have optimistically updated our cached data we can now move on and look at our thin success callback

 // you can add code here to show alert 
 // or trigger side effects
 const successCb = () => {};

In Worst Case Scenario 🤒

But let’s say our API failed and we optimistically updated our data, WHAT NOW? Here we will use our previousValue that we returned at onMutate.

onError: (error, variables, context) => {
  queryClient.setQueryData(["get-planets", context?.previousValue]);
  if (context.errorCb) {
    context.errorCb(error);
   }
},

We have now returned back to our previousValue if our API fails and that to in a snap.

MOMENT OF TRUTH 😬

Wrap Up

Well, that was smooth right? Or maybe if you don’t see the difference, imagine fetching very heavy sets of data every time you update something. You can inspect and check this on network calls as well as we are just making only one network call now. I personally like this tidy facility by react-query to improve your application performance. Here’s to being optimistic in life as well. Sit tight, this is not it. Keep learning. This is not a goodbye but take care.

Avatar photo
👋 Hey, I'm Hasan Shahid
Hi I'm Hasan! I'm a software engineer with decent experience in full stack web development. My tech stack includes JS/TS, React, Native and anything JS. I keep learning and love football.

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.