Skip to content

settleUntil:'networkidle' races React startTransition mutations; docs + a React-idle settle #18

@jbrecht

Description

@jbrecht

Problem

settleUntil: 'networkidle' can resolve in the gap before a React startTransition-deferred server action dispatches its request, so for transition-driven UI it can settle on the pre-mutation state.

Real case from umami's run-an-event: the Status control is a controlled <select> whose onChange does startTransition(async () => { await updateStatus(); router.refresh() }). After selectOption, at the instant run() returns, React hasn't dispatched the action's fetch yet — so the page is momentarily "network-idle" and settleUntil:'networkidle' can resolve against the old value before the refresh repaints.

It happened to land correctly in the full render (the request was in flight by the time Playwright polled), but it's timing-dependent — and the preview PNG for that step showed the pre-settle transient ("Save the date" instead of "Tickets open"), which is how I noticed.

Why it matters

startTransition + Server Action + router.refresh() is the standard Next.js App Router mutation pattern, so this isn't an edge case — it's the common case for any "click a control, server updates, UI repaints" step. networkidle is the natural thing an author reaches for there, and it's subtly the wrong tool.

Suggested direction

Two parts:

  1. Docs: the new "Settling" section should explicitly steer transition-deferred mutations to a real waitFor on committed UI state (e.g. expect(locator).toHaveValue(...) / wait for the badge to flip), not networkidle.
  2. Maybe a stronger settle: a settleUntil variant that also waits for React to go idle (or that waits a tick for in-flight transitions to dispatch before checking network), so the obvious option is also the robust one.

Follow-up to #14.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationImprovements or additions to documentation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions