Loading...
Loading...
Guides proper usage of the useCallback hook in React. Use this skill when optimizing function references, passing callbacks to memoized components, or preventing unnecessary re-renders.
npx skill4agent add flpbalada/my-opencode-config react-use-callbackuseCallbackmemo()import { useCallback, memo } from 'react';
const ExpensiveChild = memo(function ExpensiveChild({ onClick }) {
// Expensive rendering logic
return <button onClick={onClick}>Click me</button>;
});
function Parent({ productId }) {
// Without useCallback, handleClick would be a new function every render
// causing ExpensiveChild to re-render unnecessarily
const handleClick = useCallback(() => {
console.log('Clicked:', productId);
}, [productId]);
return <ExpensiveChild onClick={handleClick} />;
}useEffectfunction ChatRoom({ roomId }) {
const createOptions = useCallback(() => {
return { serverUrl: 'https://localhost:1234', roomId };
}, [roomId]);
useEffect(() => {
const options = createOptions();
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [createOptions]);
}function ChatRoom({ roomId }) {
useEffect(() => {
// Function defined inside effect - no useCallback needed
function createOptions() {
return { serverUrl: 'https://localhost:1234', roomId };
}
const options = createOptions();
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [roomId]);
}function useRouter() {
const { dispatch } = useContext(RouterStateContext);
const navigate = useCallback((url) => {
dispatch({ type: 'navigate', url });
}, [dispatch]);
const goBack = useCallback(() => {
dispatch({ type: 'back' });
}, [dispatch]);
return { navigate, goBack };
}// Before: todos is a dependency
const handleAddTodo = useCallback((text) => {
setTodos([...todos, { id: nextId++, text }]);
}, [todos]);
// After: No todos dependency needed
const handleAddTodo = useCallback((text) => {
setTodos(todos => [...todos, { id: nextId++, text }]);
}, []);memo()useCallback// useCallback is pointless here
function Parent() {
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
// Child will re-render anyway when Parent re-renders
return <Child onClick={handleClick} />;
}// Overkill for simple navigation
function App() {
const [page, setPage] = useState('home');
// Not needed - page transitions are inherently expensive anyway
const navigate = useCallback((page) => setPage(page), []);
return <Navigation onNavigate={navigate} />;
}// Instead of memoizing onClick
function Panel({ children }) {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
{isOpen && children}
</div>
);
}
// Children don't re-render when Panel's state changes
<Panel>
<ExpensiveComponent />
</Panel>// Don't lift state higher than necessary
function SearchForm() {
// Local state doesn't trigger parent re-renders
const [query, setQuery] = useState('');
return <input value={query} onChange={e => setQuery(e.target.value)} />;
}// Returns a new function every render
const handleClick = useCallback(() => {
doSomething();
}); // Missing dependency array!
// Correct
const handleClick = useCallback(() => {
doSomething();
}, []);// Can't call hooks in loops
function List({ items }) {
return items.map(item => {
// WRONG
const handleClick = useCallback(() => sendReport(item), [item]);
return <Chart key={item.id} onClick={handleClick} />;
});
}
// Correct: Extract to component
function List({ items }) {
return items.map(item => (
<Report key={item.id} item={item} />
));
}
function Report({ item }) {
const handleClick = useCallback(() => sendReport(item), [item]);
return <Chart onClick={handleClick} />;
}
// Alternative: Wrap Report in memo instead
const Report = memo(function Report({ item }) {
function handleClick() {
sendReport(item);
}
return <Chart onClick={handleClick} />;
});| Hook | Caches | Use Case |
|---|---|---|
| The function itself | Callback props |
| Result of calling function | Computed values |
// Equivalent
const memoizedFn = useCallback(fn, deps);
const memoizedFn = useMemo(() => fn, deps);memo()memo()const handleSubmit = useCallback((orderDetails) => {
// ...
}, [productId, referrer]);
console.log([productId, referrer]);Object.is(temp1[0], temp2[0]); // First dependency same?
Object.is(temp1[1], temp2[1]); // Second dependency same?useCallback