fix: capture AbortController in local variable to avoid race condition in three hooks
TASK-ABORT-03: Fix stale abortRef read in .finally() callbacks In useGlobalConfig, useServerSettings, and useJailConfigDetail hooks, the .finally() block was reading abortRef.current instead of using the locally captured controller reference. If load() is called while a fetch is in flight, the previous fetch's .finally() would read the new controller (not aborted) and prematurely clear the loading state while the new fetch is still pending. Changes: - useGlobalConfig.ts: use locally-captured ctrl in .finally() (line 46) - useServerSettings.ts: use locally-captured ctrl in .finally() (line 50) - useJailConfigDetail.ts: use locally-captured ctrl in .finally() (line 47) All three hooks already use ctrl correctly in .then() and .catch() callbacks. Documentation: - Add 'AbortController in Hooks' section to Web-Development.md - Explains the pattern and shows incorrect vs correct examples - Prevents future regressions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,35 +1,3 @@
|
||||
### TASK-ABORT-02 — Hooks Create `AbortController` but Never Forward Signal to API
|
||||
|
||||
**Where found**
|
||||
Several hooks correctly create a controller and check `signal.aborted` in callbacks, but then call the API function without passing the signal — so the fetch is never actually cancelled:
|
||||
|
||||
| Hook | API call missing signal |
|
||||
|------|------------------------|
|
||||
| `frontend/src/hooks/useConfigActiveStatus.ts` | `fetchJails()`, `fetchJailConfigs()` |
|
||||
| `frontend/src/hooks/useMapData.ts` | `fetchBansByCountry(range, origin, source, countryCode)` |
|
||||
| `frontend/src/hooks/useDashboardCountryData.ts` | `fetchBansByCountry(timeRange, origin, source)` |
|
||||
| `frontend/src/hooks/useImportLog.ts` | `fetchImportLog(page, pageSize, sourceId)` |
|
||||
| `frontend/src/hooks/useJailAdmin.ts` | `fetchInactiveJails()` |
|
||||
|
||||
**Goal**
|
||||
After completing TASK-ABORT-01, update each hook listed above to pass `controller.signal` (or `ctrl.signal`) to the API call. For example in `useMapData`:
|
||||
```ts
|
||||
fetchBansByCountry(range, origin, source, countryCode, controller.signal)
|
||||
```
|
||||
|
||||
**Possible traps and issues**
|
||||
- This task is a dependency of TASK-ABORT-01: the API functions must accept `signal` first.
|
||||
- `useConfigActiveStatus` makes two sequential fetches; both need the same signal forwarded.
|
||||
- After adding the signal parameter, ensure the abort check in `.then()` / `.catch()` callbacks uses `controller.signal.aborted` (the local capture), not `abortRef.current?.signal.aborted` (the mutable ref).
|
||||
|
||||
**Docs changes needed**
|
||||
None beyond TASK-ABORT-01.
|
||||
|
||||
**Why this is needed**
|
||||
The abort guard (`if (controller.signal.aborted) return;`) is present but pointless when the HTTP request itself was never aborted. The network request still completes and consumes server resources; only the state update is skipped.
|
||||
|
||||
---
|
||||
|
||||
### TASK-ABORT-03 — Stale `abortRef` Read in `.finally()` in Three Hooks
|
||||
|
||||
**Where found**
|
||||
|
||||
Reference in New Issue
Block a user