Hooks
React Hooks are powerful, but when misused, they can introduce messy, difficult to trace bugs. The goal is to use hooks thoughtfully, following React’s philosophy while keeping our codebase clean, predictable, and maintainable. This section focuses on how we use hooks effectively in practice, beyond what is already covered in React’s official documentation.
Custom hooks
Custom hooks let us encapsulate and reuse logic across components. But they should be created only when they add real value.
Guidelines:
Use custom hooks to extract logic.
- Use only when needed: Create a custom hook if:
- The logic is reused across multiple components, or
- The logic inside a single component is growing too long or cluttered.
- Organize by feature or domain: Co-locate feature-specific hooks with their feature folder.
- Naming convention: Always start with “use”. Describe what the hook does, not how it works.
- ✅ Good: useDebouncedValue, useUserSession
- ❌Bad: useStuff, useUtils
- Keep focused: Avoid “god hooks” that handle multiple concerns at once. If a hook is managing too many responsibilities, break it down into smaller, single-purpose hooks.
Example
// Focused custom hook
export function useDebouncedValue<T>( value: T, delay = 300 ) {
const [ debouncedValue, setDebouncedValue ] = useState( value );
useEffect( () => {
const handler = setTimeout( () => setDebouncedValue( value ), delay );
return () => clearTimeout( handler );
}, [ value, delay ] );
return debouncedValue;
}
Built-in hooks: usage philosophy
We trust React’s defaults, so use built-in hooks naturally, while avoiding these common anti-patterns:
useEffectuseEffectisn’t for everything: If you’re reaching for ‘useEffect‘ to sync props or derive values, reconsider. Can it be a computed value instead?- Use for side effects only (e.g., data fetching, subscriptions, timers).
useCallback&useMemo- Avoid defensive memoization: Developers often wrap functions in
useCallbackor values inuseMemojust because “it feels right” or “to be safe.” .
Only use them when required. For example:- Passing functions to memoized children that would otherwise cause re-renders. If you’re passing a function to a regular child who is not memoized, no need for useCallback.
- Running expensive calculations that actually impact performance. If the calculation is fast and simple, no need for useMemo.
- You’re not seeing re-render or performance issues? Don’t pre-optimize.
- Avoid defensive memoization: Developers often wrap functions in
useRef
Correct use cases:
- Accessing DOM elements.
- Storing stable values across renders (like IDs or cached data).
Misuse to avoid:
- Tracking layout measurements (like position or size) or time-sensitive states (like tracking when something happened). They are better handled with effects or state. While
useRefdoes give you a way to store mutable values without triggering re-renders, using it for layout or time-sensitive logic can quickly get messy or fragile. - Acting as a “dumping ground” for logic that doesn’t belong in the state.
- New React 19 hooks:
- React 19 introduces new hooks (
useFormStatus,useOptimistic, etc.) , Evaluate them where they improve readability or performance, but avoid premature adoption.
- React 19 introduces new hooks (
Common pitfalls to avoid
- Missing cleanup in useEffect: Always clear timers, subscriptions, or event listeners to prevent memory leaks.
- Unstable or missing dependencies: Never disable
react-hooks/exhaustive-depslinting without strong justification. Instead, restructure or memoize dependencies. - Overloaded effects: If a single
useEffecthandles multiple responsibilities, split them into smaller effects or extract them into a custom hook. - Conditional hook calls: Hooks must always run in the same order. Never call hooks inside if, for, or nested functions. Keep them at the top level of your component or custom hook.
Key takeaways
- Start simple: Use built-in hooks naturally, without over-optimizing.
- Extract smartly: Write custom hooks when logic is reused or overly complex.
- Keep it predictable: Avoid side effects in rendering, unstable dependencies, and unnecessary memoization.
- Follow the rules: Hooks must always be called in the same order, no exceptions.







