Standardise AbortController cancellation in setup and server health hooks

Add abortable API signals for setup status and server health/log fetches, document hook cancellation patterns, and cover stale refresh cancellation with tests.
This commit is contained in:
2026-04-21 17:38:35 +02:00
parent cf5a000bf5
commit e683108965
7 changed files with 203 additions and 12 deletions

View File

@@ -4,7 +4,7 @@
* Exposes the current setup completion status and a submission handler.
*/
import { useCallback, useEffect, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { ApiError } from "../api/client";
import { handleFetchError } from "../utils/fetchError";
import { getSetupStatus, submitSetup } from "../api/setup";
@@ -36,24 +36,42 @@ export function useSetup(): UseSetupResult {
const [error, setError] = useState<string | null>(null);
const [submitting, setSubmitting] = useState(false);
const [submitError, setSubmitError] = useState<string | null>(null);
const abortRef = useRef<AbortController | null>(null);
const refresh = useCallback(async (): Promise<void> => {
abortRef.current?.abort();
const controller = new AbortController();
abortRef.current = controller;
const { signal } = controller;
setLoading(true);
setError(null);
try {
const resp = await getSetupStatus();
const resp = await getSetupStatus(signal);
if (signal.aborted) {
return;
}
setStatus(resp);
} catch (err: unknown) {
if (signal.aborted) {
return;
}
const fallback = "Failed to fetch setup status";
handleFetchError(err, setError, fallback);
} finally {
setLoading(false);
if (!signal.aborted) {
setLoading(false);
}
}
}, []);
useEffect(() => {
void refresh();
return (): void => {
abortRef.current?.abort();
};
}, [refresh]);
const submit = useCallback(async (payload: SetupRequest): Promise<void> => {