arbret/frontend/app/hooks/useDebouncedValidation.ts
counterweight a6fa6a8012
Refactor API layer into structured domain-specific modules
- Created new api/ directory with domain-specific API modules:
  - api/client.ts: Base API client with error handling
  - api/auth.ts: Authentication endpoints
  - api/exchange.ts: Exchange/price endpoints
  - api/trades.ts: User trade endpoints
  - api/profile.ts: Profile management endpoints
  - api/invites.ts: Invite endpoints
  - api/admin.ts: Admin endpoints
  - api/index.ts: Centralized exports

- Migrated all API calls from ad-hoc api.get/post/put to typed domain APIs
- Updated all imports across codebase
- Fixed test mocks to use new API structure
- Fixed type issues in validation utilities
- Removed old api.ts file

Benefits:
- Type-safe endpoints (no more string typos)
- Centralized API surface (easy to discover endpoints)
- Better organization (domain-specific modules)
- Uses generated OpenAPI types automatically
2025-12-25 20:32:11 +01:00

57 lines
1.7 KiB
TypeScript

import { useEffect, useRef, useState } from "react";
/**
* Hook for debounced form validation.
* Validates form data after the user stops typing for a specified delay.
*
* @param formData - The form data to validate
* @param validator - Function that validates the form data and returns field errors
* @param delay - Debounce delay in milliseconds (default: 500)
* @returns Object containing current errors and a function to manually trigger validation
*/
export function useDebouncedValidation<
T,
E extends Record<string, string | undefined> = Record<string, string>,
>(
formData: T,
validator: (data: T) => E,
delay: number = 500
): {
errors: E;
setErrors: React.Dispatch<React.SetStateAction<E>>;
validate: (data?: T) => void;
} {
const [errors, setErrors] = useState<E>({} as E);
const validationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const formDataRef = useRef<T>(formData);
// Keep formDataRef in sync with formData
useEffect(() => {
formDataRef.current = formData;
}, [formData]);
// Cleanup timeout on unmount
useEffect(() => {
return () => {
if (validationTimeoutRef.current) {
clearTimeout(validationTimeoutRef.current);
}
};
}, []);
const validate = (data?: T) => {
// Clear any pending validation timeout
if (validationTimeoutRef.current) {
clearTimeout(validationTimeoutRef.current);
}
// Debounce validation - wait for user to stop typing
validationTimeoutRef.current = setTimeout(() => {
const dataToValidate = data ?? formDataRef.current;
const newErrors = validator(dataToValidate);
setErrors(newErrors);
}, delay);
};
return { errors, setErrors, validate };
}