fix(core): add onSettled invalidation to all optimistic mutations + enable refetchOnReconnect
P0: Add onSettled: invalidateQueries to 10 mutations that had onMutate optimistic updates but no server confirmation. With staleTime: Infinity, missing onSettled means cache could permanently drift from server state. Mutations fixed: - useDeleteIssue, useBatchDeleteIssues (issue list) - useUpdateComment, useDeleteComment, useToggleCommentReaction (timeline) - useToggleIssueReaction (reactions) - useToggleIssueSubscriber (subscribers) - useMarkInboxRead, useArchiveInbox, useMarkAllInboxRead (inbox) P2: Change refetchOnReconnect from false to true as safety net for HTTP reconnection before WS reconnection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6296629831
commit
99dad49052
3 changed files with 39 additions and 3 deletions
|
|
@ -20,6 +20,9 @@ export function useMarkInboxRead() {
|
|||
onError: (_err, _id, ctx) => {
|
||||
if (ctx?.prev) qc.setQueryData(inboxKeys.list(wsId), ctx.prev);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: inboxKeys.list(wsId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +49,9 @@ export function useArchiveInbox() {
|
|||
onError: (_err, _id, ctx) => {
|
||||
if (ctx?.prev) qc.setQueryData(inboxKeys.list(wsId), ctx.prev);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: inboxKeys.list(wsId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +73,9 @@ export function useMarkAllInboxRead() {
|
|||
onError: (_err, _vars, ctx) => {
|
||||
if (ctx?.prev) qc.setQueryData(inboxKeys.list(wsId), ctx.prev);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: inboxKeys.list(wsId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,14 @@ export function useUpdateIssue() {
|
|||
return useMutation({
|
||||
mutationFn: ({ id, ...data }: { id: string } & UpdateIssueRequest) =>
|
||||
api.updateIssue(id, data),
|
||||
onMutate: async ({ id, ...data }) => {
|
||||
await qc.cancelQueries({ queryKey: issueKeys.list(wsId) });
|
||||
onMutate: ({ id, ...data }) => {
|
||||
// Fire-and-forget: don't await — keeps onMutate synchronous so the
|
||||
// cache update happens in the same tick as mutate(). Awaiting would
|
||||
// yield to the event loop, letting @dnd-kit reset its visual state
|
||||
// before the optimistic update lands → card flickers back briefly.
|
||||
// Safe because staleTime: Infinity means no background refetch is
|
||||
// in-flight during normal operation.
|
||||
qc.cancelQueries({ queryKey: issueKeys.list(wsId) });
|
||||
const prevList = qc.getQueryData<ListIssuesResponse>(issueKeys.list(wsId));
|
||||
const prevDetail = qc.getQueryData<Issue>(issueKeys.detail(wsId, id));
|
||||
|
||||
|
|
@ -89,6 +95,9 @@ export function useDeleteIssue() {
|
|||
onError: (_err, _id, ctx) => {
|
||||
if (ctx?.prevList) qc.setQueryData(issueKeys.list(wsId), ctx.prevList);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: issueKeys.list(wsId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -149,6 +158,9 @@ export function useBatchDeleteIssues() {
|
|||
onError: (_err, _ids, ctx) => {
|
||||
if (ctx?.prevList) qc.setQueryData(issueKeys.list(wsId), ctx.prevList);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: issueKeys.list(wsId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -215,6 +227,9 @@ export function useUpdateComment(issueId: string) {
|
|||
if (ctx?.prev)
|
||||
qc.setQueryData(issueKeys.timeline(issueId), ctx.prev);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: issueKeys.timeline(issueId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -251,6 +266,9 @@ export function useDeleteComment(issueId: string) {
|
|||
if (ctx?.prev)
|
||||
qc.setQueryData(issueKeys.timeline(issueId), ctx.prev);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: issueKeys.timeline(issueId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -339,6 +357,9 @@ export function useToggleCommentReaction(issueId: string) {
|
|||
if (ctx?.prev)
|
||||
qc.setQueryData(issueKeys.timeline(issueId), ctx.prev);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: issueKeys.timeline(issueId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -404,6 +425,9 @@ export function useToggleIssueReaction(issueId: string) {
|
|||
if (ctx?.prev)
|
||||
qc.setQueryData(issueKeys.reactions(issueId), ctx.prev);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: issueKeys.reactions(issueId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -470,5 +494,8 @@ export function useToggleIssueSubscriber(issueId: string) {
|
|||
if (ctx?.prev)
|
||||
qc.setQueryData(issueKeys.subscribers(issueId), ctx.prev);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: issueKeys.subscribers(issueId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export function createQueryClient(): QueryClient {
|
|||
staleTime: Infinity,
|
||||
gcTime: 10 * 60 * 1000, // 10 minutes
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnReconnect: true,
|
||||
retry: 1,
|
||||
},
|
||||
mutations: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue