Give each row identity and a revision

Keep a stable row ID from the client or service, plus a revision that increases when the operator changes that row. When validation starts, capture the current revision and a new validation-attempt ID. When a response arrives, apply it only if its ID, revision, and attempt ID match the current row. Consume that attempt after completion so a late duplicate cannot overwrite a newer outcome.

React documents useReducer as a way to put state update logic in one reducer. The important idea here is the state contract, not the specific hook: a result is information about a particular row revision.

Separate validation from import

Use states such as draft, pending, accepted, and rejected for a review stage. A row becoming accepted means only that the known validation result belongs to the visible revision. It does not mean a server imported the row. Keep final import outcome, retry, and reconciliation under a separate operation identity.

The included model exercises current acceptance, current rejection, a stale result after editing, a same-revision retry whose first response arrives late, independent rows, and the rule that every row must be accepted before submission:

npm test --prefix sites/reactjsx.com/evidence/P122

It is not browser-tested JSX and does not issue requests, parse CSV, or authorize an import.

Communicate the current status

Show status next to the row it describes, including a readable reason for a current rejection and a keyboard-reachable way to correct it. Here is illustrative JSX only; it has not been browser- or assistive-technology-tested:

<tr aria-describedby={`row-${row.id}-status`}>
  <td><input aria-label={`Value for row ${row.id}`} value={row.value} onChange={onEdit} /></td>
  <td id={`row-${row.id}-status`} role="status">
    {row.status === "pending" ? "Checking this row" : row.message ?? row.status}
  </td>
  <td><button type="button" onClick={onEdit}>Correct row</button></td>
</tr>

W3C's status-message guidance explains that visible status messages which do not change context should be programmatically determinable for assistive technologies. Test the concrete markup in the target browsers and assistive technologies; this state model alone does not establish that result.

For the single-field form version of the stale-result problem, see Ignore Stale Async Validation Results in a React Form. For an operation whose final outcome is unknown, see Prevent a Double Submit While an Operation Outcome Is Unresolved.

Can the client decide that a row is safe to import?

No. The client can present a review state. The trusted service must validate the submitted data and enforce authorization again at the import boundary.