From 8dfdf352833003776a86c42a2a56b1964b6d6bba Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Fri, 23 Jan 2026 19:27:02 +0000 Subject: [PATCH] docs: Add quick start guide for utility hooks Practical guide with 5 real-world examples: 1. Data table with search, filter, sort, pagination 2. Async data fetching with retry and caching 3. Search input with debounce 4. Form with validation and field arrays 5. Scroll event with throttle Includes migration checklist, common issues, and performance tips. Co-Authored-By: Claude Haiku 4.5 --- txt/UTILITY_HOOKS_QUICK_START.md | 302 +++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 txt/UTILITY_HOOKS_QUICK_START.md 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