useState
Managing state requires understanding how React batches updates and handles asynchronous behavior.
- Functional Updates: To retrieve the latest state immediately within an update, use the functional update pattern: ```
setCounter(prev => {
console.log(prev); // Always reflects the current state
return prev + 1;
});
- Local Variables: If a variable does not impact the UI (render), do not use
useState. Use standard variables oruseRefto avoid unnecessary re-renders. - Lazy Initialization: If the initial state calculation is expensive, pass a function to
useStateto ensure it only executes during the component's initial mount. - State Immutability: React compares states by reference. When updating objects or arrays, always pass a new reference (e.g., using the spread operator or libraries like Immer). Directly modifying the state object will not trigger a re-render.
useRef
- DOM Access in Lists: Use a callback ref patern to manage an array of DOM elements dynamically: ```
const refs = useRef([]);
// In JSX: <div ref={el => refs.current[i] = el} />
- Instance Isolation: Always declare
useRefinside the component body to ensure each instance of the component has its own isolated storage. Never declare refs in the module scope. - Non-Reactive Storage:
useRefis perfect for storing data that persists across renders without triggering them.
useEffect
- Handling Closures: A common pitfall is accessing stale state inside an effect. ```
// Instead of relying on closure, use the functional update
useEffect(() => {
const timer = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
- Strict Dependency Management: React 18's Strict Mode executes effects twice in development to identify potential memory leaks. Ensure your cleanup functions properly tear down observers or timers.
- Async Functions:
useEffectcannot return a promise. Wrap asynchronous logic in an internal function: ``` useEffect(() => { const fetchData = async () => { const data = await fetch(url).then(res => res.json()); setData(data); }; fetchData(); }, []); - Avoiding Cycles: If an effect updates state that it also depends on, use the functional setter pattern to avoid listing the state in the dependency array.