Skip to content

Bug: useOptimistic fails to revert when independent async actions overlap due to global scope entanglement #36318

@acefolioDev

Description

@acefolioDev

There is a correctness bug in useOptimistic where the "revert" mechanism is gated by a global (per-root) async action reference count rather than being scoped to the specific action or transition that triggered it.

In an application where two unrelated components trigger async actions, a "Slow" action in Component A will prevent the optimistic UI in Component B from reverting or finalizing, even after Component B's action has fully resolved or failed.

The Root Cause

The issue is located in ReactFiberAsyncAction.js. The implementation currently uses module-level variables to track the state of "entangled" actions:

// From ReactFiberAsyncAction.js
let currentEntangledPendingCount: number = 0;
let currentEntangledListeners: Array<() => mixed> | null = null;

As noted in the source comments, React currently entangles all concurrent async actions because it lacks a mechanism like AsyncContext to distinguish between them. While this entanglement might be an intentional trade-off for scheduling, it breaks the functional requirements of useOptimistic. Specifically, pingEngtangledActionScope only notifies listeners (which useOptimistic relies on) when the global currentEntangledPendingCount returns to zero

Reproduction

I have verified this behavior in a Vite-based React environment.
https://codesandbox.io/p/sandbox/frosty-https-d46684

Metadata

Metadata

Assignees

No one assigned

    Labels

    Status: UnconfirmedA potential issue that we haven't yet confirmed as a bug

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions