---
description: Prevent double-click / duplicate API calls from frontend UI
alwaysApply: true
---

# Prevent duplicated API calls (frontend)

When wiring UI actions (buttons, row actions, dialogs) to backend APIs, **always prevent double submission**. Relying on `setState` + `disabled` alone is not sufficient because rapid double-click can fire twice before React re-renders.

- **Must**: add an **in-flight lock** (e.g. `useRef(false)`) and early-return if already running.
- **Must**: keep the UI disabled/loading (`disabled={isLoading}`) for user feedback.
- **Must**: clear the lock in `finally` so it always releases.
- **Should**: if the same endpoint can be triggered from multiple places, consider a shared “single-flight” helper (dedupe by `method+url+body` key).

Example pattern:

```tsx
const inFlightRef = useRef(false);
const [isSaving, setIsSaving] = useState(false);

const onSave = async () => {
  if (inFlightRef.current) return;
  inFlightRef.current = true;
  setIsSaving(true);
  try {
    await doRequest();
  } finally {
    setIsSaving(false);
    inFlightRef.current = false;
  }
};
```

