-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Description
In this reduced test case I'm seeing some odd behaviour when combining React Query with React Actions/Transitions and useOptimistic
.
- We have a query that returns a count, starting at 1.
- When a user clicks this button, we run an action to add 1 to the count. The action does the following:
- optimistically adds 1 to the count
- server mutation to update the server-side count
- refetch query for count
If we click the button once, I expect the count to show 2.
However, the actual behaviour is this:
- Count shows 2 (optimistic update).
- Then count briefly shows 3 when the query refetch finishes.
- Then count reverts to 2.
Screen.Recording.2025-10-09.at.18.36.15.mov
My investigation
Looking at the logs, I believe this is what is happening:
- Server state begins at 1 ✅
- User clicks button. Optimistic count is now server state + 1 = 2. ✅
- Refetch finishes. React Query synchronously updates. The new server state is 2. React rebases the optimistic state on top of the new server state: server state + 1 = 3. ❌
- Finally, action finishes so React drops optimistic state and count reverts back to 2. ✅

If my hypothesis is correct, I believe this happens because React Query uses useSyncExternalStore
. As per the docs:
If the store is mutated during a non-blocking Transition update, React will fall back to performing that update as blocking.
If that is the case then I guess we can't really expect React Query to work with actions/transitions/useOptimistic
? Has this issue come up before and are there any plans to resolve this? 🙏
For comparison, the same test case but without React Query works with no such problems:
- Server state begins at 1 ✅
- User clicks button. Optimistic count is now server state + 1 = 2. ✅
- Refetch finishes. The new server state is 2 but the state update is part of the transition, so it doesn't happen synchronously. Optimistic count continues to show 2. ✅
- Finally, action finishes so React drops optimistic state and count reverts back to server state (2). ✅
Your minimal, reproducible example
https://codesandbox.io/p/sandbox/react-dev-forked-lhxlvx
TanStack Query version
5.90.2