Now that #344 is merged, all data-loading useEffect effects follow this pattern:
useEffect(() => {
let cancelled = false;
void (async () => {
if (!cancelled) {
await loadData();
}
})();
return () => {
cancelled = true;
};
}, [loadData]);
We already have hooks for forms, feature flags, and user roles, e.g.:
Let's consider extracting a custom hook like useEffectAsync/useAsyncEffect/useFetchEffect to DRY up this repeated pattern:
Possible API (naming bikeshedding allowed):
useEffectAsync(callback, deps)
- Accepts an async callback
- Handles cancellation logic
Example:
useEffectAsync(async () => { await loadData(); }, [loadData])
- Optionally allow returning a teardown/cleanup function
Value:
- Deduplicate ~10+ effect bodies project-wide
- Less chance of forgetting cancellation
- Clearer intent in new code
Acceptance Criteria
Let's use this issue to bikeshed names, agree on ergonomic API, and track progress.
Related hooks for design reference:
Now that #344 is merged, all data-loading
useEffecteffects follow this pattern:We already have hooks for forms, feature flags, and user roles, e.g.:
frontend/src/hooks/useRoleGate.tsfrontend/src/hooks/useFeatureFlag.tsfrontend/src/hooks/useForm.tsLet's consider extracting a custom hook like
useEffectAsync/useAsyncEffect/useFetchEffectto DRY up this repeated pattern:Possible API (naming bikeshedding allowed):
Example:
Value:
Acceptance Criteria
useEffectAsync(or similar) is implemented and testeduseEffectcalls in data-loading pages are migratedLet's use this issue to bikeshed names, agree on ergonomic API, and track progress.
Related hooks for design reference:
frontend/src/hooks/useRoleGate.tsfrontend/src/hooks/useFeatureFlag.tsfrontend/src/hooks/useForm.ts