Performance: Custom hook best practices
Custom hooks affect interaction responsiveness and rendering efficiency by managing state and side effects cleanly.
function useData() { const [data, setData] = React.useState(null); React.useEffect(() => { fetch('/api/data').then(res => res.json()).then(setData); }, []); return data; }
function useData() { const [data, setData] = React.useState(null); fetch('/api/data').then(res => res.json()).then(setData); return data; }
| Pattern | DOM Operations | Reflows | Paint Cost | Verdict |
|---|---|---|---|---|
| Fetching data in hook body | Minimal DOM changes | Multiple re-renders | Multiple paints | [X] Bad |
| Fetching data inside useEffect | Minimal DOM changes | Single re-render | Single paint | [OK] Good |
| State update in hook body | No DOM changes | Infinite re-renders | Continuous paints | [X] Bad |
| State update inside useEffect | No DOM changes | Single re-render | Single paint | [OK] Good |
| Inline function in hook | No DOM changes | Extra child re-renders | Extra paints | [X] Bad |
| Memoized function with useCallback | No DOM changes | Minimal re-renders | Minimal paints | [OK] Good |
use so React can identify them as hooks.use to follow React conventions correctly follows this convention by starting the name with use.use to follow React conventions -> Option Ause and return state and updater properly. function useCount() { const [count, setCount] = useState(0); return [count, () => setCount(count + 1)]; } uses useCount and returns an array with count and increment function.useState correctly and returns a function that updates state with setCount(count + 1). This is the correct pattern.function useToggle(initial) {
const [on, setOn] = useState(initial);
const toggle = () => setOn(prev => !prev);
return [on, toggle];
}
function Component() {
const [isOn, toggle] = useToggle(false);
toggle();
return {isOn ? 'ON' : 'OFF'};
}toggle() once?toggle() is called directly in the component body, which executes during every render phase.toggle() is unconditional, it flips state every render, causing an infinite loop. React prohibits this and produces errors/warnings (e.g., "Cannot update a component while rendering a different component" or too many re-renders).function useCounter() {
let count = 0;
const increment = () => { count += 1; };
return { count, increment };
}count that resets to 0 on every call. It does not use useState, so React won't track changes or re-render.useState, updating count does not cause component updates, making the hook ineffective.useFetch that fetches data from a URL and returns loading state, data, and error. Which approach follows best practices?useEffect to avoid running on every render. State variables track loading, data, and error.useEffect inside the hook to fetch data on mount, use useState for loading, data, and error, and return them as an object correctly uses useEffect and useState to manage asynchronous fetch and returns all relevant states as an object for clarity.useEffect inside the hook to fetch data on mount, use useState for loading, data, and error, and return them as an object -> Option BPass the test and this pattern moves unknown