To remember in 5 seconds:
Is your calculation slow? (filtering, sorting, calculating a cart total) -> useMemo
Are you passing a function to a child component that is already optimized? -> useCallback
And the golden rule: don't use them everywhere. If a calculation takes 2ms, there’s no point. Use them only when you actually have a performance issue—not by default.
const filteredProducts = products.filter(p => p.price < 100) // computationally expensive
// With useMemo: it only re-filters if 'products' changes
const filteredProducts = useMemo(() => {
return products.filter(p => p.price < 100)
}, [products]) // dependency
// Without useCallback: a new function is created on every render -> the CartButton component re-renders unnecessarily
function addToCart() { ... }
// With useCallback: the same function is kept in memory
const addToCart = useCallback(() => {
// add-to-cart logic
}, [cart]) // the function is only recreated if 'cart' changes