# React Usecallback

> Use when writing or reviewing useCallback usage in React components. Covers React Compiler impact, when useCallback is justified, and the most common mistake (useCallback without memo).

- Skill: `b4r7x/react-usecallback` (Agent Skill)
- Install (CLI): `npx skillmds@latest add b4r7x/react-usecallback`
- Raw SKILL.md: https://api.skillmd.com/api/skills/b4r7x/react-usecallback/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: b4r7x (https://skillmd.com/u/b4r7x)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/b4r7x/react-usecallback

---


# React useCallback

## Overview

useCallback memoizes function references between renders. In 2026 with React Compiler, **manual useCallback is rarely needed** — the compiler auto-memoizes. Without the compiler, useCallback only helps when paired with `memo()` on the child component.

## With React Compiler (2025+)

React Compiler 1.0 (Oct 2025) auto-applies memoization at build time. **Don't add new useCallback** — the compiler handles it better. If memoization is unnecessary, the compiler omits it from output.

**Exceptions where manual useCallback is still needed:**
- External libraries not compiled by React Compiler (older react-hook-form, animation libs)
- Functions used as `useEffect` dependencies that can't be moved inside the effect
- Custom hooks exporting functions that consumers demonstrably need stable — the fn lands in consumers' effect deps or memo'd children (see scope note below)
- Measured performance issues the compiler doesn't fix

## Without React Compiler

useCallback only makes sense **together with `memo()` on the child component**.

```jsx
// ❌ useCallback without memo = dead code
function Parent() {
  const handleClick = useCallback(() => {
    console.log('click');
  }, []);
  return <Child onClick={handleClick} />;
  // Child re-renders anyway because Parent re-renders
}

// ✅ useCallback + memo = Child skips re-render
const Child = memo(function Child({ onClick }) {
  return <button onClick={onClick}>Click</button>;
});

function Parent() {
  const handleClick = useCallback(() => {
    console.log('click');
  }, []);
  return <Child onClick={handleClick} />;
  // Child does NOT re-render — handleClick has stable reference
}
```

## Valid Use Cases

### 1. useEffect dependency
```jsx
const fetchData = useCallback(async () => {
  const data = await fetch('/api/users');
}, []);

useEffect(() => {
  fetchData();
}, [fetchData]); // stable reference = runs once
```

### 2. Custom hook exports — scope note

```jsx
function useCart() {
  const [items, setItems] = useState([]);
  // ✅ Stable reference — consumers put addItem in effect deps
  const addItem = useCallback((item) => {
    setItems(prev => [...prev, item]);
  }, []);
  return { items, addItem };
}
```

This case is justified when you can NAME the consumer that needs the stability (the fn goes into a consumer's `useEffect` deps, a `memo()`'d child, or context value). For **library hooks other developers consume**, the doctrine is the opposite: start unmemoized, plain functions, and add useCallback only when a profiler or a concrete consumer requirement demands it — that territory belongs to `react-hook-authoring`, and its rules win there. This skill governs app-internal component code.

### 3. Context provider functions
```jsx
function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const logout = useCallback(() => setUser(null), []);
  const value = useMemo(() => ({ user, logout }), [user, logout]);
  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
```

## Decision

| Question | Answer |
|----------|--------|
| Using React Compiler? | Don't add useCallback manually |
| Passing fn to `memo()`'d child? | ✅ useCallback |
| Function in useEffect deps? | ✅ useCallback (or move fn inside effect) |
| Exporting fn from an app-internal hook, consumer needs stability? | ✅ useCallback |
| Authoring a library hook for other developers? | ❌ Start plain — `react-hook-authoring` governs |
| Event handler without memo on child? | ❌ Skip — it's dead code |

## Common Mistake

The most over-used pattern: wrapping every event handler in useCallback "for performance" without memo on the child. The React docs say explicitly: **use useCallback only for performance optimization, not as a default way to declare functions**.

## References
- [useCallback — React docs](https://react.dev/reference/react/useCallback) — official API reference with usage examples and caveats
- [Understanding useMemo and useCallback](https://www.joshwcomeau.com/react/usememo-and-usecallback/) — Josh Comeau's deep dive on when memoization actually helps
- [React Compiler](https://react.dev/learn/react-compiler) — official guide on how React Compiler replaces manual useCallback

