React Hooks Deep Dive
Understand how React hooks work under the hood, master advanced patterns, and avoid common pitfalls in state and effect management.
FrontendIntermediate18 min read
AI Summary
Quick context for React Hooks Deep Dive
React Hooks Deep Dive is a intermediate guide in Frontend. Understand how React hooks work under the hood, master advanced patterns, and avoid common pitfalls in state and effect management.. Key topics: hooks, performance, react, state-management.
## How Hooks Work Internally
React hooks rely on a linked list of hook calls stored on the fiber node. Each render calls hooks in the same order, which is why hooks must not be called conditionally.
```javascript
// React stores hooks as a linked list on the fiber
// Simplified internal representation:
let hooks = [];
let currentHookIndex = 0;
function useState(initialValue) {
const hook = hooks[currentHookIndex] || { state: initialValue };
hooks[currentHookIndex] = hook;
currentHookIndex++;
const setState = (newValue) => {
hook.state = newValue;
scheduleRerender();
};
return [hook.state, setState];
}
```
Understanding this explains why the Rules of Hooks exist — breaking call order corrupts the linked list.
## useState Patterns
### Lazy Initialization
When initial state depends on expensive computation or props, pass a function to `useState`.
```javascript
function Counter({ initialCount }) {
// Only runs on first render
const [count, setCount] = useState(() => {
return initialCount + computeExpensiveValue();
});
return ;
}
```
### Functional Updates
Always use the callback form when new state depends on previous state to avoid stale closures.
```javascript
// Wrong — may use stale state
setCount(count + 1);
// Correct — always uses latest state
setCount(prev => prev + 1);
```
## useEffect Lifecycle
`useEffect` combines `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount` into a single API.
```javascript
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchUser() {
const resp fetch(`/api/users/${userId}`);
const data = await response.json();
if (!cancelled) setUser(data);
}
fetchUser();
// Cleanup function — runs before next effect and on unmount
return () => { cancelled = true; };
}, [userId]); // Re-run when userId changes
if (!user) return ;
return
);
}
```
## useMemo and useCallback
These hooks memoize values and functions to prevent unnecessary re-renders of child components.
```javascript
function ProductList({ products, filter }) {
// Only recompute when products or filter changes
const filtered = useMemo(() => {
return products.filter(p => p.category === filter);
}, [products, filter]);
// Stable reference prevents child re-renders
const handleClick = useCallback((id) => {
console.log(`Selected product ${id}`);
}, []);
return (
{user.name}
;
}
```
The cleanup function prevents state updates on unmounted components and cancels outdated async work.
## useRef for Mutable Values
`useRef` persists values across renders without triggering re-renders. It is the escape hatch for DOM access and mutable instance variables.
```javascript
function Stopwatch() {
const [time, setTime] = useState(0);
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
setTime(t => t + 1);
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
useEffect(() => {
return () => clearInterval(intervalRef.current);
}, []);
return (
{time}s
-
{filtered.map(p => (
Continue with related content
Suggested next reads and tools from the knowledge graph.