2025-12-26 18:49:00 +01:00
|
|
|
import { useState, useEffect, useCallback, useRef } from "react";
|
Refactor frontend: Add reusable hooks and components
- Created useAsyncData hook: Eliminates repetitive data fetching boilerplate
- Handles loading, error, and data state automatically
- Supports enabled/disabled fetching
- Provides refetch function
- Created PageLayout component: Standardizes page structure
- Handles loading state, authorization checks, header, error display
- Reduces ~10 lines of boilerplate per page
- Created useMutation hook: Simplifies action handling
- Manages loading state and errors for mutations
- Supports success/error callbacks
- Used for cancel, create, revoke actions
- Created ErrorDisplay component: Standardizes error UI
- Consistent error banner styling across app
- Integrated into PageLayout
- Created useForm hook: Foundation for form state management
- Handles form data, validation, dirty checking
- Ready for future form migrations
- Migrated pages to use new patterns:
- invites/page.tsx: useAsyncData + PageLayout
- trades/page.tsx: useAsyncData + PageLayout + useMutation
- trades/[id]/page.tsx: useAsyncData
- admin/price-history/page.tsx: useAsyncData + PageLayout
- admin/invites/page.tsx: useMutation for create/revoke
Benefits:
- ~40% reduction in boilerplate code
- Consistent patterns across pages
- Easier to maintain and extend
- Better type safety
All tests passing (32 frontend, 33 e2e)
2025-12-25 21:30:35 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook for fetching async data with loading and error states.
|
|
|
|
|
* Handles the common pattern of fetching data when component mounts or dependencies change.
|
|
|
|
|
*
|
|
|
|
|
* @param fetcher - Function that returns a Promise with the data
|
|
|
|
|
* @param options - Configuration options
|
|
|
|
|
* @returns Object containing data, loading state, error, and refetch function
|
|
|
|
|
*/
|
|
|
|
|
export function useAsyncData<T>(
|
|
|
|
|
fetcher: () => Promise<T>,
|
|
|
|
|
options: {
|
|
|
|
|
/** Whether the fetch should be enabled (default: true) */
|
|
|
|
|
enabled?: boolean;
|
|
|
|
|
/** Callback for handling errors */
|
|
|
|
|
onError?: (err: unknown) => void;
|
|
|
|
|
/** Initial data value (useful for optimistic updates) */
|
|
|
|
|
initialData?: T;
|
|
|
|
|
} = {}
|
|
|
|
|
): {
|
|
|
|
|
data: T | null;
|
|
|
|
|
isLoading: boolean;
|
|
|
|
|
error: string | null;
|
|
|
|
|
refetch: () => Promise<void>;
|
|
|
|
|
} {
|
|
|
|
|
const { enabled = true, onError, initialData } = options;
|
|
|
|
|
|
|
|
|
|
const [data, setData] = useState<T | null>(initialData ?? null);
|
|
|
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
|
|
2025-12-26 18:49:00 +01:00
|
|
|
// Use ref to store the latest fetcher function to avoid dependency issues
|
|
|
|
|
const fetcherRef = useRef(fetcher);
|
|
|
|
|
const onErrorRef = useRef(onError);
|
|
|
|
|
|
|
|
|
|
// Update refs when values change
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
fetcherRef.current = fetcher;
|
|
|
|
|
onErrorRef.current = onError;
|
|
|
|
|
}, [fetcher, onError]);
|
|
|
|
|
|
Refactor frontend: Add reusable hooks and components
- Created useAsyncData hook: Eliminates repetitive data fetching boilerplate
- Handles loading, error, and data state automatically
- Supports enabled/disabled fetching
- Provides refetch function
- Created PageLayout component: Standardizes page structure
- Handles loading state, authorization checks, header, error display
- Reduces ~10 lines of boilerplate per page
- Created useMutation hook: Simplifies action handling
- Manages loading state and errors for mutations
- Supports success/error callbacks
- Used for cancel, create, revoke actions
- Created ErrorDisplay component: Standardizes error UI
- Consistent error banner styling across app
- Integrated into PageLayout
- Created useForm hook: Foundation for form state management
- Handles form data, validation, dirty checking
- Ready for future form migrations
- Migrated pages to use new patterns:
- invites/page.tsx: useAsyncData + PageLayout
- trades/page.tsx: useAsyncData + PageLayout + useMutation
- trades/[id]/page.tsx: useAsyncData
- admin/price-history/page.tsx: useAsyncData + PageLayout
- admin/invites/page.tsx: useMutation for create/revoke
Benefits:
- ~40% reduction in boilerplate code
- Consistent patterns across pages
- Easier to maintain and extend
- Better type safety
All tests passing (32 frontend, 33 e2e)
2025-12-25 21:30:35 +01:00
|
|
|
const fetchData = useCallback(async () => {
|
|
|
|
|
if (!enabled) return;
|
|
|
|
|
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setError(null);
|
|
|
|
|
|
|
|
|
|
try {
|
2025-12-26 18:49:00 +01:00
|
|
|
const result = await fetcherRef.current();
|
Refactor frontend: Add reusable hooks and components
- Created useAsyncData hook: Eliminates repetitive data fetching boilerplate
- Handles loading, error, and data state automatically
- Supports enabled/disabled fetching
- Provides refetch function
- Created PageLayout component: Standardizes page structure
- Handles loading state, authorization checks, header, error display
- Reduces ~10 lines of boilerplate per page
- Created useMutation hook: Simplifies action handling
- Manages loading state and errors for mutations
- Supports success/error callbacks
- Used for cancel, create, revoke actions
- Created ErrorDisplay component: Standardizes error UI
- Consistent error banner styling across app
- Integrated into PageLayout
- Created useForm hook: Foundation for form state management
- Handles form data, validation, dirty checking
- Ready for future form migrations
- Migrated pages to use new patterns:
- invites/page.tsx: useAsyncData + PageLayout
- trades/page.tsx: useAsyncData + PageLayout + useMutation
- trades/[id]/page.tsx: useAsyncData
- admin/price-history/page.tsx: useAsyncData + PageLayout
- admin/invites/page.tsx: useMutation for create/revoke
Benefits:
- ~40% reduction in boilerplate code
- Consistent patterns across pages
- Easier to maintain and extend
- Better type safety
All tests passing (32 frontend, 33 e2e)
2025-12-25 21:30:35 +01:00
|
|
|
setData(result);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
const errorMessage = err instanceof Error ? err.message : "Failed to load data";
|
|
|
|
|
setError(errorMessage);
|
2025-12-26 18:49:00 +01:00
|
|
|
if (onErrorRef.current) {
|
|
|
|
|
onErrorRef.current(err);
|
Refactor frontend: Add reusable hooks and components
- Created useAsyncData hook: Eliminates repetitive data fetching boilerplate
- Handles loading, error, and data state automatically
- Supports enabled/disabled fetching
- Provides refetch function
- Created PageLayout component: Standardizes page structure
- Handles loading state, authorization checks, header, error display
- Reduces ~10 lines of boilerplate per page
- Created useMutation hook: Simplifies action handling
- Manages loading state and errors for mutations
- Supports success/error callbacks
- Used for cancel, create, revoke actions
- Created ErrorDisplay component: Standardizes error UI
- Consistent error banner styling across app
- Integrated into PageLayout
- Created useForm hook: Foundation for form state management
- Handles form data, validation, dirty checking
- Ready for future form migrations
- Migrated pages to use new patterns:
- invites/page.tsx: useAsyncData + PageLayout
- trades/page.tsx: useAsyncData + PageLayout + useMutation
- trades/[id]/page.tsx: useAsyncData
- admin/price-history/page.tsx: useAsyncData + PageLayout
- admin/invites/page.tsx: useMutation for create/revoke
Benefits:
- ~40% reduction in boilerplate code
- Consistent patterns across pages
- Easier to maintain and extend
- Better type safety
All tests passing (32 frontend, 33 e2e)
2025-12-25 21:30:35 +01:00
|
|
|
} else {
|
|
|
|
|
console.error("Failed to fetch data:", err);
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
2025-12-26 18:49:00 +01:00
|
|
|
}, [enabled]);
|
Refactor frontend: Add reusable hooks and components
- Created useAsyncData hook: Eliminates repetitive data fetching boilerplate
- Handles loading, error, and data state automatically
- Supports enabled/disabled fetching
- Provides refetch function
- Created PageLayout component: Standardizes page structure
- Handles loading state, authorization checks, header, error display
- Reduces ~10 lines of boilerplate per page
- Created useMutation hook: Simplifies action handling
- Manages loading state and errors for mutations
- Supports success/error callbacks
- Used for cancel, create, revoke actions
- Created ErrorDisplay component: Standardizes error UI
- Consistent error banner styling across app
- Integrated into PageLayout
- Created useForm hook: Foundation for form state management
- Handles form data, validation, dirty checking
- Ready for future form migrations
- Migrated pages to use new patterns:
- invites/page.tsx: useAsyncData + PageLayout
- trades/page.tsx: useAsyncData + PageLayout + useMutation
- trades/[id]/page.tsx: useAsyncData
- admin/price-history/page.tsx: useAsyncData + PageLayout
- admin/invites/page.tsx: useMutation for create/revoke
Benefits:
- ~40% reduction in boilerplate code
- Consistent patterns across pages
- Easier to maintain and extend
- Better type safety
All tests passing (32 frontend, 33 e2e)
2025-12-25 21:30:35 +01:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
fetchData();
|
|
|
|
|
}, [fetchData]);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
data,
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
|
|
|
|
refetch: fetchData,
|
|
|
|
|
};
|
|
|
|
|
}
|