Skip to content

Is React Query incompatible with React Actions/Transitions/useOptimistic? #9742

@OliverJAsh

Description

@OliverJAsh

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:
    1. optimistically adds 1 to the count
    2. server mutation to update the server-side count
    3. refetch query for count

If we click the button once, I expect the count to show 2.

However, the actual behaviour is this:

  1. Count shows 2 (optimistic update).
  2. Then count briefly shows 3 when the query refetch finishes.
  3. 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:

  1. Server state begins at 1 ✅
  2. User clicks button. Optimistic count is now server state + 1 = 2. ✅
  3. 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. ❌
  4. Finally, action finishes so React drops optimistic state and count reverts back to 2. ✅
Image

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:

  1. Server state begins at 1 ✅
  2. User clicks button. Optimistic count is now server state + 1 = 2. ✅
  3. 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. ✅
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions