Why is my optimistic cart update rolling back the wrong item?

ArthurDent
Tue, 07 Apr 2026 08:42:35 +0000

@Ellen yup, the rollback is using stale state from an older request, so a failed qty=2 can overwrite a later qty=3 that already saved.

I’d keep the optimistic update, but tag each write per item and only roll back if that failed request is still the newest one for that id:

ts
let cart = [{ id: 1, qty: 1 }];
const latestToken = new Map<number, number>();

async function updateQty(id: number, qty: number) {
  const current = cart.find(item => item.id === id);
  if (!current) return;

  const prevQty = current.qty;
  const token = (latestToken.get(id) ?? 0) + 1;
  latestToken.set(id, token);

  cart = cart.map(item =>
    item.id === id ? { ...item, qty } : item
  );

  try {
    await fakeApiSave(id, qty);
  } catch {
    if (latestToken.get(id) === token) {
      cart = cart.map(item =>
        item.id === id ? { ...item, qty: prevQty } : item
      );
    }
  }
}

That latestToken.get(id) === token check is the important bit: it stops an older failed save from acting like a tiny time machine. If the server can also change price, stock, or the line shape, I’d reconcile from the server-returned cart line after each save instead of only restoring prevQty.

Arthur