Keep draft and remote state distinct
Maintain a stable record ID, the base revision from which the local draft began,
the latest remote revision and value, a local draft, a dirty marker, and an
explicit conflict state. If a newer snapshot arrives while the draft is clean,
the screen can adopt it and advance its base revision. If it arrives while the
draft is dirty, retain the local value and its original base revision, record
the newer remote revision, and surface a resolution choice.
Do not silently merge fields. A meaningful merge requires product-specific knowledge about each field, validation, user intent, and server behavior. A generic reducer is safer when it reports that a refresh arrived rather than pretending it reconciled a record.
Make the state contract inspectable
The local model in evidence/P109 tests the narrow rule:
npm test --prefix sites/reactjsx.com/evidence/P109
It accepts a newer snapshot only for a clean draft. For a dirty draft it retains
the local value and base revision, exposes the newer remote revision, and uses
refresh-arrived—not “merged”—as the state. An explicit discard then adopts
the recorded remote value. A snapshot for another record or an older revision
cannot alter the draft.
Give the operator a real next action
When a conflict state is visible, explain whether the operator may keep editing, discard their draft, reload, or use a product-specific resolve flow. Associate the status with the editing region and preserve keyboard access to the available choice. Review the actual browser and assistive-technology behavior before claiming that a particular announcement works.
This pattern does not fetch data, cancel requests, merge values, authorize a save, or prevent a server-side conflict. It prevents one narrow interface failure: a background response quietly erasing work that the operator still needs to resolve. For a related revision-matching pattern, see Ignore Stale Async Validation Results in a React Form.
Is a dirty boolean enough for every form?
No. Complex forms may need field-level identity or a product-specific resolution model. A record-level dirty state is useful only when the team has decided what the explicit resolution actions mean.
Should the screen always block editing after a refresh?
Not necessarily. The product can allow continued editing, but it should make the newer remote state and eventual save behavior explicit rather than hiding the possibility of a conflict.