Loading skill
Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Standardized guidelines and patterns for Frontend React Hooks Custom.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 10% | 0% |
| case-07 | ✓→✗ | ▼ Worse | 76% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 72% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 73% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 62% | 0% |
use prefixAll hooks MUST start with use:
typescript// ✅ Good useUser useLocalStorage usePrevious // ❌ Bad getUser localStorage previous
typescriptexport function useToggle(initialValue = false) { const [value, setValue] = useState(initialValue); const toggle = useCallback(() => { setValue(v => !v); }, []); const setTrue = useCallback(() => setValue(true), []); const setFalse = useCallback(() => setValue(false), []); return { value, toggle, setTrue, setFalse }; } // Usage function Component() { const modal = useToggle(); return ( <> <button onClick={modal.setTrue}>Open</button> {modal.value && <Modal onClose={modal.setFalse} />} </> ); }
typescriptinterface UseQueryOptions<T> { enabled?: boolean; refetchOnMount?: boolean; onSuccess?: (data: T) => void; onError?: (error: Error) => void; } export function useQuery<T>( key: string, fetcher: () => Promise<T>, options: UseQueryOptions<T> = {} ) { const [data, setData] = useState<T | null>(null); const [error, setError] = useState<Error | null>(null); const [isLoading, setIsLoading] = useState(false); const execute = useCallback(async () => { setIsLoading(true); setError(null); try { const result = await fetcher(); setData(result); options.onSuccess?.(result); } catch (err) { const error = err as Error; setError(error); options.onError?.(error); } finally { setIsLoading(false); } }, [fetcher, options]); useEffect(() => { if (options.enabled !== false) { execute(); } }, [key, options.enabled, execute]); const refetch = useCallback(() => { return execute(); }, [execute]); return { data, error, isLoading, refetch }; } // Usage function UserProfile({ userId }: { userId: string }) { const { data: user, isLoading, error, refetch } = useQuery( `user-${userId}`, () => fetchUser(userId), { onSuccess: (user) => console.log('User loaded:', user), onError: (err) => console.error('Failed:', err) } ); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return <div>{user?.name}</div>; }
typescriptexport function useForm<T extends Record<string, any>>(initialValues: T) { const [values, setValues] = useState(initialValues); const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({}); const [touched, setTouched] = useState<Partial<Record<keyof T, boolean>>>({}); const handleChange = useCallback((name: keyof T, value: any) => { setValues(prev => ({ ...prev, [name]: value })); }, []); const handleBlur = useCallback((name: keyof T) => { setTouched(prev => ({ ...prev, [name]: true })); }, []); const setFieldError = useCallback((name: keyof T, error: string) => { setErrors(prev => ({ ...prev, [name]: error })); }, []); const reset = useCallback(() => { setValues(initialValues); setErrors({}); setTouched({}); }, [initialValues]); return { values, errors, touched, handleChange, handleBlur, setFieldError, reset }; } // Usage function LoginForm() { const form = useForm({ email: '', password: '' }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!form.values.email) { form.setFieldError('email', 'Email is required'); return; } // Submit logic }; return ( <form onSubmit={handleSubmit}> <input value={form.values.email} onChange={e => form.handleChange('email', e.target.value)} onBlur={() => form.handleBlur('email')} /> {form.errors.email && <span>{form.errors.email}</span>} </form> ); }
typescriptexport function useLocalStorage<T>(key: string, initialValue: T) { const [storedValue, setStoredValue] = useState<T>(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch (error) { console.error(error); return initialValue; } }); const setValue = useCallback((value: T | ((val: T) => T)) => { try { const valueToStore = value instanceof Function ? value(storedValue) : value; setStoredValue(valueToStore); window.localStorage.setItem(key, JSON.stringify(valueToStore)); } catch (error) { console.error(error); } }, [key, storedValue]); return [storedValue, setValue] as const; } // Usage function ThemeSwitcher() { const [theme, setTheme] = useLocalStorage('theme', 'light'); return ( <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}> Current: {theme} </button> ); }
typescriptexport function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(handler); }; }, [value, delay]); return debouncedValue; } // Usage function SearchInput() { const [search, setSearch] = useState(''); const debouncedSearch = useDebounce(search, 500); useEffect(() => { if (debouncedSearch) { // Fetch results console.log('Searching for:', debouncedSearch); } }, [debouncedSearch]); return <input value={search} onChange={e => setSearch(e.target.value)} />; }
typescriptexport function usePrevious<T>(value: T): T | undefined { const ref = useRef<T>(); useEffect(() => { ref.current = value; }, [value]); return ref.current; } // Usage function Counter() { const [count, setCount] = useState(0); const prevCount = usePrevious(count); return ( <div> <p>Current: {count}</p> <p>Previous: {prevCount}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }
use@testing-library/react-hooksOther measured skills in the registry, with their headline benchmark lift.