diff --git a/txt/UTILITY_HOOKS_QUICK_START.md b/txt/UTILITY_HOOKS_QUICK_START.md new file mode 100644 index 000000000..5658c2c47 --- /dev/null +++ b/txt/UTILITY_HOOKS_QUICK_START.md @@ -0,0 +1,302 @@ +# Utility Hooks Quick Start Guide + +## Installation + +Both packages are already part of the monorepo. Just import them: + +```typescript +import { useTableState, useAsyncOperation, useDebounced, useThrottled } from '@metabuilder/hooks-utils' +import { useFormBuilder } from '@metabuilder/hooks-forms' +``` + +## Common Patterns + +### 1. Data Table with Search, Filter, Sort, Pagination + +```typescript +import { useTableState } from '@metabuilder/hooks-utils' + +interface User { + id: number + name: string + email: string + status: 'active' | 'inactive' +} + +function UserTable({ users }: { users: User[] }) { + const table = useTableState(users, { + pageSize: 10, + searchFields: ['name', 'email'], + defaultSort: { field: 'name', direction: 'asc' } + }) + + return ( + <> + table.setSearch(e.target.value)} + /> + + + + + + + + {table.paginatedItems.map(user => ( + + + + + + ))} + +
{user.name}{user.email}{user.status}
+ +
+ + Page {table.currentPage} of {table.totalPages} + +
+ + ) +} +``` + +### 2. Async Data Fetching with Retry + +```typescript +import { useAsyncOperation } from '@metabuilder/hooks-utils' + +function UserProfile({ userId }: { userId: string }) { + const { data: user, isLoading, error, retry } = useAsyncOperation( + () => fetch(`/api/users/${userId}`).then(r => r.json()), + { + retryCount: 3, + retryDelay: 1000, + cacheKey: `user-${userId}`, + cacheTTL: 60000, // 1 minute + } + ) + + useEffect(() => { + // Auto-execute on mount + const loadUser = async () => { + await execute() + } + loadUser() + }, []) + + if (isLoading) return + if (error) return + if (!user) return + + return +} +``` + +### 3. Search Input with Debounce + +```typescript +import { useDebounced } from '@metabuilder/hooks-utils' + +function SearchUsers() { + const [query, setQuery] = useState('') + const { value: debouncedQuery, isPending } = useDebounced(query, 300) + + useEffect(() => { + if (debouncedQuery) { + searchUsers(debouncedQuery) + } + }, [debouncedQuery]) + + return ( + <> + setQuery(e.target.value)} + placeholder="Search..." + /> + {isPending && } + + ) +} +``` + +### 4. Form with Validation and Field Arrays + +```typescript +import { useFormBuilder } from '@metabuilder/hooks-forms' + +interface UserForm { + name: string + email: string + emails: string[] +} + +function UserForm() { + const form = useFormBuilder({ + initialValues: { + name: '', + email: '', + emails: [''] + }, + validation: (values) => { + const errors: ValidationErrors = {} + if (!values.name) errors.name = 'Name required' + if (!values.email.includes('@')) errors.email = 'Invalid email' + return errors + }, + onSubmit: async (values) => { + await submitUser(values) + }, + validateOnBlur: true, + }) + + const emailArray = form.getFieldArray('emails') + + return ( +
{ e.preventDefault(); form.submit() }}> +
+ form.setFieldValue('name', e.target.value)} + onBlur={() => form.setFieldTouched('name')} + /> + {form.touched.name && form.errors.name && ( + {form.errors.name} + )} +
+ +
+ form.setFieldValue('email', e.target.value)} + onBlur={() => form.setFieldTouched('email')} + /> + {form.touched.email && form.errors.email && ( + {form.errors.email} + )} +
+ +
+ Additional Emails + {emailArray.values.map((email, idx) => ( +
+ { + const newEmails = [...emailArray.values] + newEmails[idx] = e.target.value + form.setFieldValue('emails', newEmails) + }} + /> + +
+ ))} + +
+ + + + {form.submitError && ( +
{form.submitError}
+ )} +
+ ) +} +``` + +### 5. Scroll Event with Throttle + +```typescript +import { useThrottled } from '@metabuilder/hooks-utils' + +function ScrollIndicator() { + const [scrollY, setScrollY] = useState(0) + const { value: throttledY } = useThrottled(scrollY, 100) + + useEffect(() => { + const handleScroll = () => setScrollY(window.scrollY) + window.addEventListener('scroll', handleScroll) + return () => window.removeEventListener('scroll', handleScroll) + }, []) + + useEffect(() => { + // This fires at most every 100ms + updateScrollBar(throttledY) + }, [throttledY]) + + return
+} +``` + +## Migration Checklist + +### When to use these hooks: + +- **useTableState**: Replace manual pagination, sorting, filtering, search logic +- **useAsyncOperation**: Replace manual Promise handling and retry logic +- **useDebounced**: Replace manual debounce in search/filter inputs +- **useThrottled**: Replace manual throttle in scroll/resize handlers +- **useFormBuilder**: Replace custom form state management and validation + +### How to migrate: + +1. Identify duplicate logic in your component +2. Import the appropriate hook +3. Replace manual state with hook calls +4. Delete old validation/state management code +5. Test that functionality still works + +## Common Issues + +### Q: useTableState resets to page 1 when I search +**A**: This is intentional - when filters change, page should reset. If you want different behavior, use `setPage` after `setSearch`. + +### Q: useAsyncOperation caching not working +**A**: Make sure you provide a unique `cacheKey` and set `cacheTTL` (time-to-live in ms). Cache is global per key. + +### Q: useFormBuilder validation fires before I'm done typing +**A**: Use `validateOnChange: false` and `validateOnBlur: true` for better UX. + +### Q: useDebounced is still calling too often +**A**: Increase the delay value (in milliseconds). 300ms is good for most cases. + +### Q: Can I use these with Redux? +**A**: Yes! These hooks are compatible with Redux apps. Just dispatch actions in `onSubmit` callbacks. + +## Performance Tips + +1. **useTableState**: Re-renders only visible items, not all 1000+ rows +2. **useAsyncOperation**: Use `cacheKey` to avoid refetching same data +3. **useDebounced**: Higher delay (500-1000ms) = fewer API calls for search +4. **useThrottled**: Lower interval (50-100ms) for smooth scroll/drag +5. **useFormBuilder**: Use `getFieldError` selectors to avoid full re-renders + +## Related Documentation + +- Full API Reference: `redux/hooks-utils/README.md` +- Form Examples: `redux/hooks-forms/README.md` +- Architecture: `txt/UTILITY_HOOKS_IMPLEMENTATION_2026-01-23.md` +- Project Guide: `CLAUDE.md` → "Utility Hooks" section