skip to Main Content

I have a React 18 application and using React Query.

When I use queryClient.invalidateQueries({ queryKey: ['anecdotes'] }); after the PUT request that causes the change, the application makes a new GET request to retrieve the query data from the server. I am trying to avoid this extra HTTP GET request. So, I am trying to use queryClient.setQueryData to make an update instead. How can I achieve this?

App.jsx

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { getAnecdotes, updateAnecdote } from './requests';
import AnecdoteForm from './components/AnecdoteForm';
import Notification from './components/Notification';

const App = () => {
  const queryClient = useQueryClient();
  
  const updateAnecdoteMutation = useMutation({
    mutationFn: updateAnecdote,
    onSuccess: () => {
      // I am currently doing this; it causes an extra network request
      queryClient.invalidateQueries({ queryKey: ['anecdotes'] });
      // HOW DO I USE THE queryClient.setQueryData HERE?
      // queryClient.setQueryData(['anecdotes']);
    },
  });

  const result = useQuery({
    queryKey: ['anecdotes'],
    queryFn: getAnecdotes,
    retry: false,
  });
  console.log(JSON.parse(JSON.stringify(result)));

  if (result.isLoading) {
    return <div>loading data...</div>;
  }

  if (result.isError) {
    return <div>anecdote service not available due to problems in server</div>;
  }

  const anecdotes = result.data;

  const handleVote = (anecdote) => {
    console.log('vote', anecdote);
    updateAnecdoteMutation.mutate({ ...anecdote, votes: anecdote.votes + 1 });
  }

  return (
    <div>
      <h3>Anecdote app</h3>

      <Notification />
      <AnecdoteForm />

      {anecdotes.map(anecdote =>
        <div key={anecdote.id}>
          <div>
            {anecdote.content}
          </div>
          
          <div>
            has {anecdote.votes}
            <button onClick={() => handleVote(anecdote)}>vote</button>
          </div>
        </div>
      )}
    </div>
  );
}

export default App;

requests.js file:

import axios from 'axios';

const baseUrl = 'http://localhost:3001/anecdotes';

export const getAnecdotes = () =>
  axios.get(baseUrl).then(res => res.data);

export const createAnecdote = newAnecdote =>
  axios.post(baseUrl, newAnecdote).then(res => res.data);

export const updateAnecdote = updatedAnecdote =>
  axios.put(`${baseUrl}/${updatedAnecdote.id}`, updatedAnecdote).then(res => res.data);

2

Answers


  1. Basically that would be something like this

        onSuccess: (newData) => {
          queryClient.setQueryData(['anecdotes'], oldData => {
            return newData;
          });
        },
    

    Assuming you have same response from the mutation as for the get request (like get one entity / put one entity).

    If you have get list of entities and want to update the list when you mutate only one entity – you need some extra logic to do a partial update based on some id.
    Usually you’d want to use a key to distinguish requests and understand which ones you want to update and which ones you want to omit.

        onSuccess: (newData) => {
          queryClient.setQueryData(['anecdotes'], oldData => {
            const anecdoteObj = oldData.find(anecdote => anecdote.id === newData.id);
            Object.keys(newData).forEach(newKey => {
              anecdoteObj[newKey] = newData[newKey]; // keeping pointer to object
            })
            return [...oldData]; // Creating a copy to trigger rerender on partial update
          });
        },
    
    Login or Signup to reply.
  2. setQueryData allows to directly access the current cached data through its updater function, so replace the anecdote with the matching ID with updatedAnecdote. this way no extra get call is made

    const updateAnecdoteMutation = useMutation({
    mutationFn: updateAnecdote,
    onSuccess: (updatedAnecdote) => {
    queryClient.setQueryData(['anecdotes'], (oldAnecdotes) => {
      if (!oldAnecdotes) return [];
    
      // Create a new array with the updated anecdote
      return oldAnecdotes.map(anecdote =>
        anecdote.id === updatedAnecdote.id ? updatedAnecdote : anecdote
        );
       });
      },
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search