Card briefly disappears after optimistic update while other items shift #9220
Replies: 2 comments
-
what should I be seeing in that video? it looks fine to me :/
Your code is totally unformatted, but from what I can see, there are some places where you mutate things in place:
not sure if this is local mutation or mutates something we have in the cache, but note that all updates must happen immutably to guarantee anything: |
Beta Was this translation helpful? Give feedback.
-
Between the 5th and 7th second of the video, you can notice a small jump in the second column where the card is being dropped. When I release the card, it briefly disappears, and the one that was previously below it jumps back into place. Then the dragged card reappears. This issue doesn’t occur when managing state locally, but it does happen when relying on the cache. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi, I'm using React Query for an optimistic update when dragging a card between columns (Kanban-style). I'm also using @hello-pangea/dnd for drag-and-drop between columns.
In onMutate, I optimistically remove the card from one column and insert it into another, using queryClient.setQueryData. I don’t call invalidateQueries in onSuccess — I want to fully rely on the optimistic state.
The issue:
When I drop the card, the other cards in the destination column first shift as if the card is not yet there
Then, after a moment (likely after setQueryData finishes), the dragged card appears in its new position
This creates a small but noticeable visual pop-in or "flash" where the card briefly disappears and then shows up
Is there any recommended way to make the moved card appear immediately in the new column — without that brief disappearance?
Thanks a lot!
Code:
import { toastError } from '@/components/toast-error'
import { toastSuccess } from '@/components/toast-success'
import { IClientReturn } from '@/types/app.types'
import {
InfiniteData,
useMutation,
useQueryClient,
} from '@tanstack/react-query'
import axios from 'axios'
import { updateClientReturnStatus } from '../api'
import {
IGetClientsByReturnStatusResp,
IParams,
} from '../api/clients/getClientsByReturnStatus'
import { IUpdateClientReturnStatusReq } from '../api/clients/updateClientReturnStatus'
export type ColumnKey = keyof Omit<
IGetClientsByReturnStatusResp,
'clients_count'
const STATUS_MAP: Record<string, string> = {
ucspecified: 'Нераспределеный',
new_lead: 'Новый лид',
not_reached: 'Недозвон',
postpone: 'Перенос сроков',
feedback_wait: 'Получить обратную связь',
warm: 'Теплый',
payment_wait: 'Ждем оплату',
closed: 'Закрыт',
}
export function useUpdateClientReturnStatus(
params: Omit<IParams, 'page' | 'elements_on_page'>
) {
const queryClient = useQueryClient()
}
import { IClientReturn } from '@/types/app.types'
import { keepPreviousData, useInfiniteQuery } from '@tanstack/react-query'
import { getClientsByReturnStatus } from '../api'
import { IParams } from '../api/clients/getClientsByReturnStatus'
const limitElementsOnPage = 20
export function useClientsByReturnStatus(
pageParams: Omit<IParams, 'page' | 'elements_on_page'>
) {
return useInfiniteQuery({
queryKey: ['clientsReturn', pageParams],
queryFn: ({ pageParam }) =>
getClientsByReturnStatus({
params: {
...pageParams,
page: pageParam,
elements_on_page: limitElementsOnPage,
},
}),
initialPageParam: 1,
getNextPageParam: (lastPage, allPages) => {
if (allPages.length * limitElementsOnPage < lastPage.clients_count || 0)
return allPages.length + 1
return undefined
},
select: data => {
const merged = data.pages.reduce<
Record<string, { items: IClientReturn[]; count: number }>
>((acc, page) => {
for (const [key, value] of Object.entries(page)) {
if (key === 'clients_count') continue
}
2025-05-28.09.35.36.mov
Beta Was this translation helpful? Give feedback.
All reactions