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:
2026-04-23 08:40:03 +02:00
parent 57ee5a2892
commit 4e3f2005f9
5 changed files with 39 additions and 35 deletions

View File

@@ -43,7 +43,7 @@ export function useGlobalConfig(): UseGlobalConfigResult {
}
})
.finally(() => {
if (!abortRef.current?.signal.aborted) {
if (!ctrl.signal.aborted) {
setLoading(false);
}
});

View File

@@ -44,7 +44,7 @@ export function useJailConfigDetail(name: string): UseJailConfigDetailResult {
}
})
.finally(() => {
if (!abortRef.current?.signal.aborted) {
if (!ctrl.signal.aborted) {
setLoading(false);
}
});

View File

@@ -47,7 +47,7 @@ export function useServerSettings(): UseServerSettingsResult {
}
})
.finally(() => {
if (!abortRef.current?.signal.aborted) {
if (!ctrl.signal.aborted) {
setLoading(false);
}
});