Part 1 was about waiting well. This one is about not waiting at all, and about a couple of mistakes I made along the way that are worth walking through in the open.

If you read Part 1 on useActionState, you already know the shape of the problem it solves: three hand-rolled state variables, a try/catch/finally, and a pending flag you're hoping stays in sync with reality. useActionState fixes that for the "wait for the server, then show the result" case, and I closed that piece by saying useOptimistic was the next hook worth learning. This is that piece. But some interactions never wanted to wait in the first place. A comment posts. A like fires. A checklist item gets ticked off. The user expects to see the outcome the instant they act, not the instant the network agrees with them.

That's the gap useOptimistic fills. And building a real example that combines it with useActionState is what exposed two mistakes I'd made without realizing it.

The old trick, and why it quietly lied

Before this hook existed, "instant" UI usually meant something like this: