Search code examples
reactjsreact-querytanstackreact-query

queryClient.setQueryData() updating issues and inconsistences in next js application


I am using @tanstack/react-query in my frontend application and I really enjoy it. I also use it as the state manager for everything that has to do with async network calls and data fetching. Right now, I am building a kanban board with drag and drop functionality and we intend to preserve the columns and the items arrangement to the backend whenver the user performs a drag and drop action.

After every drag and drop action, I manipulate the array and update the indices of the columns or items being moved. Since I want the action to be instantaneous even before I get a response from the server, I modify the state on the frontend and display the updated data arrangement to mimic an instantaneous update to the user.

To achieve this using react query, I get my query cache using queryClient.getQueryData(queryKey), manipulate the cache and then update the cache using queryClient.setQueryData(queryKey, updatedData). The issue I have is that it doesn't work and I don't understand why.

Sometimes, it works and sometimes it doesn't. I am so confused and don't know why.

const queryKey = ['fetchLists'];
const { data, isLoading } = useFetchBoard(queryKey);
const [board, setBoard] = useState<TBoard | null>(null);

const queryClient = useQueryClient();

const updateQueryData = (updatedBoard: TBoard) => {
  queryClient.setQueryData(queryKey, updatedBoard);
}

const getExistingBoard = (): TBoard => {
  return queryClient.getQueryData(queryKey) as TBoard
};


const handleOnDragEnd = () => {
 // Get existing board from query
 const existingBoard = getExistingBoard();

 if(existingBoard) {
  const newBoard = performIndicesChange(existingBoard, 1, 4);

  // Update queryData. This doesn't work.
  updateQueryData(newBoard);

  //Perform a setState. This weirdly works even though I am not using the `board` variable from // useState to populate my KanbanBoard
  setBoard(newBoard)
 }
}

return (
  <div>
    <KanbanBoard data={data} />
  </div>

)


Why does react query not detect the updated data to allow react rerender the frontend? Why does setBoard work even when I am not using board


Solution

  • When using queryClient.setQueryData make sure it expects immutable updates and make sure you do not modify the existing data in place but rather return a new object or array.

    Also make sure that performIndicesChange returns a new object or array instead of modifying the existing.

    Try updating the handleOnDragEnd method as below to see if that works

    const handleOnDragEnd = () => {
      // get what you want 
      const existingBoard = getExistingBoard();
    
      if (existingBoard) {
        const newBoard = performIndicesChange(existingBoard, 1, 4);
    
        // immutable update is happening here
        updateQueryData({ ...existingBoard, ...newBoard });
    
        // set new state
        setBoard(newBoard);
      }
    };
    

    One more thing to note here is that in certain scenarios, the change is not immediately detected when using setQueryDat as react query optimizes re-renders to avoid unnecessary renders, and using setBoard might trigger a more immediate re-render.