Skip to content

frontend: extract useAsyncData<T> hook to replace duplicated three-state load pattern (7 views) #57

Description

@countercheck

Source: codebase audit, 2026-06-11 (audit finding F3, Tier 1)
Severity: Medium
Category: DRY + state-mgmt safety

Problem

Seven admin views reinvent the same three-state load pattern: useState<T[] | null>(null) + load effect + error setter. Each view also implements its own (sometimes missing) abort-on-unmount logic.

Sites:

The MyDbAccessView.waitForPending() polling loop in particular has no AbortController and can setState after unmount.

Suggested approach

Add frontend/src/hooks/useAsyncData.ts:

function useAsyncData<T>(
  fetcher: (signal: AbortSignal) => Promise<T>,
  deps: DependencyList,
): { data: T | null; error: string | null; isLoading: boolean; refresh: () => void };

Behavior:

Colocated test: loads on mount; surfaces error on rejection; refresh() re-fetches; unmount cancels the in-flight request without React act() warnings.

Part of a cluster

This pairs naturally with #55, #56, #58, #61 — the same PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions