useOptimistic is built for exactly this, and the rollback is automatic:
When the action settles, React re-renders from the real state. If it failed, the real state never changed and the optimistic value simply disappears — you don't hand-roll the undo.
Do still surface the failure with a toast. Silently reverting a number the user just watched go up is worse than saying "couldn't save that".