Adds a navigation-aware request cancellation mechanism that automatically aborts all route-specific API requests when the user navigates to a different route. This prevents silent state-update errors from responses arriving after component unmount and conserves bandwidth by cancelling now-irrelevant requests. Key additions: - NavigationCancellationContext: Context for managing route-specific signals - NavigationCancellationProvider: Provider that detects route changes and aborts all signals from the previous route - useNavigationAbortSignal hook: Allows components to subscribe to navigation-aware cancellation signals - Comprehensive tests for the cancellation lifecycle - Documentation in Web-Development.md for request lifecycle policy The provider is placed in the app hierarchy between BrowserRouter and AuthProvider, ensuring consistent cancellation behavior across all routes. Long-lived background tasks (polling, session validation) can opt-out by managing their own AbortController lifecycle. Closes #23 from Tasks.md: No global cancellation policy on route transitions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
54 lines
1.9 KiB
TypeScript
54 lines
1.9 KiB
TypeScript
/**
|
|
* Hook to subscribe to navigation-aware request cancellation.
|
|
*
|
|
* Returns an AbortSignal that is automatically aborted when the user
|
|
* navigates to a different route. Use this signal to cancel API requests
|
|
* that are specific to the current route and should not survive a navigation.
|
|
*
|
|
* Usage:
|
|
* const signal = useNavigationAbortSignal();
|
|
* const { items } = useListData({
|
|
* fetcher: (sig) => fetchBans(sig || signal),
|
|
* // ...
|
|
* });
|
|
*
|
|
* When to use:
|
|
* - For page-level data fetches that should not persist across navigation
|
|
* - For user-initiated refetches on the current page
|
|
* - For paginated lists, search results, filters
|
|
*
|
|
* When NOT to use:
|
|
* - For long-lived background polls (use your own AbortController instead)
|
|
* - For service-level state syncs (e.g., session validation)
|
|
* - For actions that may take longer than a user interaction timeout
|
|
*
|
|
* Note: The signal may already be aborted at the time you check it,
|
|
* depending on timing. This is safe — fetchers should handle aborted
|
|
* signals gracefully by throwing/catching AbortError.
|
|
*/
|
|
|
|
import { useContext } from "react";
|
|
import { NavigationCancellationContext } from "../providers/NavigationCancellationContext";
|
|
|
|
/**
|
|
* Get an AbortSignal for the current route's request lifecycle.
|
|
*
|
|
* The returned signal will be aborted when the user navigates away.
|
|
* All requests using this signal will be automatically cancelled.
|
|
*
|
|
* @returns AbortSignal tied to the current route
|
|
* @throws Error if called outside NavigationCancellationProvider
|
|
*/
|
|
export function useNavigationAbortSignal(): AbortSignal {
|
|
const context = useContext(NavigationCancellationContext);
|
|
|
|
if (!context) {
|
|
throw new Error(
|
|
"useNavigationAbortSignal must be used within NavigationCancellationProvider. " +
|
|
"Wrap your router with <NavigationCancellationProvider> in App.tsx.",
|
|
);
|
|
}
|
|
|
|
return context.getNavigationSignal();
|
|
}
|