Approach 1 — Disable the button (simplest)
Set a loading flag. Disable the button the moment the first click fires.
When to use: Form submissions, payment buttons, any action that should fire exactly once.
Limitation: If the user navigates away and back — the state resets. Use a ref for more reliability.
Approach 2 — Debounce
Wait for the user to “stop clicking” before firing the request.
Double click within 300ms? Only the last click fires.
When to use: Search inputs, filter buttons, anything where you want the “last intent” to win.
Limitation: Adds a 300ms delay not suitable for instant actions like payments.
Approach 3 — AbortController (most powerful)
Cancel the previous in-flight request when a new one fires.
Previous request gets cancelled at the network level. Only the latest request completes.
When to use: Search-as-you-type, filter changes, any scenario where only the latest result matters.
Quick decision guide
- Form submit / Payment: Disable button
- Search input: Debounce or AbortController
- Rapid filter changes: AbortController
- General safety net: Disable button (always safe default)
Lastly,
The disable button approach covers 80% of real-world cases it’s the right default. Add AbortController when you’re dealing with fast repeated requests like search.
Next time you see a duplicate entry in your database you know exactly where to look.
Drop a reaction if this helped. More real-world frontend fixes every week 🚀



Top comments (0)