Skip to content

How to decide if useMemo and useCallback are obsolete with React Forget Compiler

The problem with manual memoization

When I look at my React codebase from 2024, I see hooks everywhere:

ProductPage.jsx (before React Compiler)
import { useMemo, useCallback } from 'react';
function ProductPage({ productId, referrer }) {
const product = useData('/product/' + productId);
const requirements = useMemo(() => {
return computeRequirements(product);
}, [product]);
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails,
});
}, [productId, referrer]);
return (
<div className={theme}>
<ShippingForm requirements={requirements} onSubmit={handleSubmit} />
</div>
);
}

Every time I modified this component, I had to think about dependency arrays. Did I add theme to the dependencies? Did I forget to update the array when I added a new variable? I spent more time managing memoization than writing features.

What React Compiler changes

React Compiler (the official name for what was called “React Forget”) automates memoization. When I enabled it on my project, I removed all the manual memoization:

ProductPage.jsx (with React Compiler)
function ProductPage({ productId, referrer }) {
const product = useData('/product/' + productId);
const requirements = computeRequirements(product);
const handleSubmit = (orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails,
});
};
return (
<div className={theme}>
<ShippingForm requirements={requirements} onSubmit={handleSubmit} />
</div>
);
}

The compiler analyzes my code and automatically memoizes requirements and handleSubmit. I don’t need dependency arrays. I don’t need to think about whether I should memoize something. The compiler handles it.

But memoization isn’t completely obsolete

When I first adopted React Compiler, I removed every useMemo and useCallback from my codebase. Then I noticed some bugs.

Bug 1: react-hook-form integration

I use react-hook-form in a form component. The watch function has interior mutability—the function reference stays the same, but the return value changes:

FormComponent.jsx (broken)
function Form() {
const { watch } = useForm();
// This never updates because watch() mutates internally
const name = useMemo(() => watch('name'), [watch]);
return <div>Name: {name}</div>;
}

I thought the compiler would handle this, but it can’t detect that watch changes internally. The solution: keep manual memoization for library-specific cases:

FormComponent.jsx (fixed)
function Form() {
const { watch } = useForm();
const name = watch('name'); // Compiler can optimize this
return <div>Name: {name}</div>;
}

Bug 2: React DnD callbacks

I use react-dnd for drag-and-drop. The library depends on reference equality for drag handlers:

DraggableItem.jsx
import { useDrag } from 'react-dnd';
function DraggableItem({ item, onDrop }) {
// Keep manual memoization for react-dnd
const dragProps = useMemo(() => ({
type: 'ITEM',
item: { id: item.id, data: item },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
}), [item.id, item.data]);
const [{ isDragging }, drag] = useDrag(() => dragProps);
const handleDrop = useCallback(() => {
onDrop(item.id);
}, [item.id, onDrop]);
return (
<div ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }}>
<button onClick={handleDrop}>Drop</button>
</div>
);
}

When I removed the memoization, drag-and-drop re-registered on every render. The compiler couldn’t detect this external dependency on reference equality.

How the compiler works

React Compiler optimizes your code through static analysis:

  1. It reads your component and builds a dependency graph
  2. It identifies values that can be memoized
  3. It injects the equivalent of useMemo/useCallback automatically
  4. It validates that your code follows the Rules of React

The key difference: the compiler’s memoization is often more precise than what I wrote manually. It doesn’t over-memoize, and it doesn’t miss dependencies.

When to use manual memoization in 2026

Based on my experience, here’s when you still need useMemo and useCallback:

Keep manual memoization for:

  • Third-party libraries that depend on reference equality (DnD libraries, animation libraries)
  • Integration with libraries that have interior mutability (react-hook-form’s watch, some state management libraries)
  • Explicit performance tuning when you’ve measured that the compiler’s heuristics need adjustment

Remove manual memoization for:

  • All typical component logic
  • Context value optimization
  • Child component callbacks (unless required by a library)

Opting out of compiler optimization

Sometimes the compiler gets it wrong. I had a component with complex state mutations that the compiler couldn’t analyze safely:

ComplexChart.jsx
function ComplexChart({ dataPoints }) {
"use no memo"; // Explicitly skip compiler optimization
const [config, setConfig] = useState({
smoothing: 0.5,
showGrid: true,
colorScheme: 'default',
});
const updateConfig = (key, value) => {
setConfig(prev => {
const next = { ...prev, [key]: value };
// Side effects that compiler can't track
if (key === 'colorScheme') {
trackAnalytics('color-change', value);
}
return next;
});
};
return <Chart data={dataPoints} config={config} />;
}

The "use no memo" directive tells the compiler to skip optimization for this component. I use this sparingly—only when the compiler causes actual problems.

Migration strategy

When I migrated my codebase to React Compiler, I followed this approach:

  1. Enable the compiler in development mode
  2. Run the app and look for console errors (the compiler validates Rules of React)
  3. Remove all useMemo and useCallback by default
  4. Add back manual memoization only when tests fail or performance degrades
  5. Use the ESLint rule react-hooks/exhaustive-deps with compiler integration to identify problematic removals

This approach worked better than trying to predict what needed memoization. The compiler told me through runtime errors and performance issues.

Summary

In this post, I showed how React Compiler eliminates most manual memoization in React applications. The key point is that useMemo and useCallback aren’t obsolete, but they’re now niche tools for specific library integrations rather than everyday optimization.

For 95% of your components, remove the memoization hooks and let the compiler handle it. Only add them back when you’re integrating with third-party libraries that depend on reference equality, or when you’ve measured a performance issue that the compiler’s heuristics don’t handle well.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments