React Compiler em 2026: O Fim do useMemo e useCallback Manual
Olá HaWkers, uma das maiores mudanças no ecossistema React em 2026 é a adoção em massa do React Compiler. A memoização manual que atormentou desenvolvedores por anos está finalmente se tornando coisa do passado.
Vamos entender o que mudou e como escrever React moderno.
O Problema Que o Compiler Resolve
A Dor da Memoização Manual
Por anos, desenvolvedores React precisaram lidar com isso:
// O inferno da memoização manual (como era antes)
import { useMemo, useCallback, memo } from 'react';
interface User {
id: number;
name: string;
email: string;
}
interface UserListProps {
users: User[];
onSelect: (user: User) => void;
filter: string;
}
// Componente memoizado
const UserCard = memo(({ user, onSelect }: {
user: User;
onSelect: (user: User) => void;
}) => {
return (
<div onClick={() => onSelect(user)}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
});
function UserList({ users, onSelect, filter }: UserListProps) {
// useMemo para filtrar users
const filteredUsers = useMemo(() => {
return users.filter(u =>
u.name.toLowerCase().includes(filter.toLowerCase())
);
}, [users, filter]);
// useCallback para estabilizar referência
const handleSelect = useCallback((user: User) => {
console.log('Selected:', user);
onSelect(user);
}, [onSelect]);
// useMemo para computação cara
const stats = useMemo(() => ({
total: users.length,
filtered: filteredUsers.length,
percentage: (filteredUsers.length / users.length * 100).toFixed(1)
}), [users.length, filteredUsers.length]);
return (
<div>
<p>Mostrando {stats.filtered} de {stats.total} ({stats.percentage}%)</p>
{filteredUsers.map(user => (
<UserCard
key={user.id}
user={user}
onSelect={handleSelect}
/>
))}
</div>
);
}Problemas dessa abordagem:
- Código verboso e difícil de ler
- Fácil esquecer dependências
- Over-memoization (memoizar coisas que não precisam)
- Under-memoization (esquecer de memoizar o que precisa)
- Difícil saber quando memoizar
O React Compiler
Como Funciona
// Com React Compiler (2026) - O MESMO código, sem memoização manual
interface User {
id: number;
name: string;
email: string;
}
interface UserListProps {
users: User[];
onSelect: (user: User) => void;
filter: string;
}
// Sem memo() - compiler otimiza automaticamente
function UserCard({ user, onSelect }: {
user: User;
onSelect: (user: User) => void;
}) {
return (
<div onClick={() => onSelect(user)}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
}
// Código limpo e natural
function UserList({ users, onSelect, filter }: UserListProps) {
// Sem useMemo - compiler detecta e otimiza
const filteredUsers = users.filter(u =>
u.name.toLowerCase().includes(filter.toLowerCase())
);
// Sem useCallback - compiler estabiliza automaticamente
const handleSelect = (user: User) => {
console.log('Selected:', user);
onSelect(user);
};
// Sem useMemo - compiler sabe que é computação derivada
const stats = {
total: users.length,
filtered: filteredUsers.length,
percentage: (filteredUsers.length / users.length * 100).toFixed(1)
};
return (
<div>
<p>Mostrando {stats.filtered} de {stats.total} ({stats.percentage}%)</p>
{filteredUsers.map(user => (
<UserCard
key={user.id}
user={user}
onSelect={handleSelect}
/>
))}
</div>
);
}O que o compiler faz:
- Analisa o código em tempo de compilação
- Detecta valores que podem ser memoizados
- Insere memoização automaticamente onde necessário
- Garante que callbacks sejam estáveis
- Otimiza re-renders automaticamente
O Que o Compiler Analisa
Detecção Inteligente
// O compiler entende padrões comuns
function ProductPage({ productId }: { productId: string }) {
const [quantity, setQuantity] = useState(1);
// Compiler detecta: depende só de productId
// Memoiza automaticamente
const product = products.find(p => p.id === productId);
// Compiler detecta: depende de product e quantity
// Memoiza e recalcula quando necessário
const totalPrice = product ? product.price * quantity : 0;
// Compiler detecta: função que é passada como prop
// Estabiliza referência automaticamente
const handleAddToCart = () => {
addToCart(productId, quantity);
};
// Compiler detecta: objeto criado no render
// Memoiza para evitar re-renders filhos
const cartItem = {
productId,
quantity,
price: totalPrice
};
return (
<div>
<h1>{product?.name}</h1>
<QuantitySelector
value={quantity}
onChange={setQuantity} // setter é estável por natureza
/>
<p>Total: ${totalPrice}</p>
<AddToCartButton
item={cartItem} // objeto memoizado
onAdd={handleAddToCart} // callback estabilizado
/>
</div>
);
}
Configuração em 2026
Setup Padrão
// next.config.ts (Next.js 15+)
import type { NextConfig } from 'next';
const config: NextConfig = {
experimental: {
// React Compiler habilitado por padrão em 2026
reactCompiler: true,
},
};
export default config;
// Para projetos Vite
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import reactCompiler from 'babel-plugin-react-compiler';
export default defineConfig({
plugins: [
react({
babel: {
plugins: [reactCompiler],
},
}),
],
});ESLint Plugin
// eslint.config.js
import reactCompiler from 'eslint-plugin-react-compiler';
export default [
{
plugins: {
'react-compiler': reactCompiler,
},
rules: {
// Avisa quando código pode ser problemático
'react-compiler/react-compiler': 'error',
},
},
];
Quando Ainda Usar Hooks Manuais
Casos Especiais
// Casos onde memoização manual ainda faz sentido
// 1. Computações MUITO caras com controle fino
function DataVisualization({ data }: { data: number[] }) {
// Para cálculos que levam segundos, você pode
// querer controle explícito
const processedData = useMemo(() => {
return expensiveStatisticalAnalysis(data);
}, [data]);
// Ou usar a nova API useDeferredValue para não bloquear
const deferredData = useDeferredValue(data);
return <Chart data={processedData} />;
}
// 2. Integração com bibliotecas externas
function MapComponent({ markers }: { markers: Marker[] }) {
// Bibliotecas externas podem ter suas próprias
// regras de comparação
const memoizedMarkers = useMemo(
() => markers.map(m => createMapMarker(m)),
[markers]
);
return <ExternalMapLibrary markers={memoizedMarkers} />;
}
// 3. Refs e imperative handles
function VideoPlayer({ src }: { src: string }) {
const videoRef = useRef<HTMLVideoElement>(null);
// useCallback ainda útil para imperative APIs
const play = useCallback(() => {
videoRef.current?.play();
}, []);
const pause = useCallback(() => {
videoRef.current?.pause();
}, []);
// Expondo métodos imperativos
useImperativeHandle(ref, () => ({
play,
pause,
}), [play, pause]);
return <video ref={videoRef} src={src} />;
}
Padrões Modernos em 2026
Server Components + Compiler
// Combinando React Server Components com Compiler
// app/products/page.tsx (Server Component)
async function ProductsPage() {
// Dados buscados no servidor
const products = await fetchProducts();
return (
<div>
<h1>Produtos</h1>
{/* Client Component recebe dados serializados */}
<ProductGrid products={products} />
</div>
);
}
// components/ProductGrid.tsx (Client Component)
'use client';
function ProductGrid({ products }: { products: Product[] }) {
const [filter, setFilter] = useState('');
const [sort, setSort] = useState<'price' | 'name'>('name');
// Compiler otimiza tudo automaticamente
const filtered = products.filter(p =>
p.name.toLowerCase().includes(filter.toLowerCase())
);
const sorted = [...filtered].sort((a, b) => {
if (sort === 'price') return a.price - b.price;
return a.name.localeCompare(b.name);
});
return (
<div>
<FilterInput value={filter} onChange={setFilter} />
<SortSelect value={sort} onChange={setSort} />
<div className="grid">
{sorted.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
</div>
);
}Actions e Mutations
// Padrão moderno com Server Actions
// actions/cart.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function addToCart(productId: string, quantity: number) {
await db.cart.add({ productId, quantity });
revalidatePath('/cart');
}
// components/AddToCartButton.tsx
'use client';
import { addToCart } from '@/actions/cart';
import { useTransition } from 'react';
function AddToCartButton({ productId }: { productId: string }) {
const [isPending, startTransition] = useTransition();
const [quantity, setQuantity] = useState(1);
// Compiler otimiza automaticamente
const handleClick = () => {
startTransition(async () => {
await addToCart(productId, quantity);
});
};
return (
<button onClick={handleClick} disabled={isPending}>
{isPending ? 'Adicionando...' : 'Adicionar ao Carrinho'}
</button>
);
}
Migrando Código Legado
Estratégia de Migração
// Passo 1: Habilite o compiler no projeto
// Passo 2: Remova memoização desnecessária gradualmente
// ANTES
const MemoizedComponent = memo(function Component({ data }) {
const processed = useMemo(() => process(data), [data]);
const handler = useCallback(() => doSomething(), []);
return <div onClick={handler}>{processed}</div>;
});
// DEPOIS
function Component({ data }) {
const processed = process(data);
const handler = () => doSomething();
return <div onClick={handler}>{processed}</div>;
}
// Passo 3: Confie no ESLint plugin para avisar problemas
// Passo 4: Teste performance antes e depoisO Que Remover
// Checklist de migração
const migrationChecklist = {
// Pode remover com segurança
safeToRemove: [
'React.memo() em componentes simples',
'useMemo() para objetos passados como props',
'useCallback() para handlers de evento',
'useMemo() para computações derivadas simples'
],
// Avaliar caso a caso
evaluate: [
'useMemo() para computações muito caras',
'useCallback() em bibliotecas externas',
'memo() com função de comparação customizada'
],
// Manter
keep: [
'useRef() - não é memoização',
'useState() - não é memoização',
'useEffect() - side effects',
'useLayoutEffect() - side effects síncronos'
]
};
Performance em 2026
Comparativo
// Resultados típicos de migração
const performanceComparison = {
bundleSize: {
before: '145kb',
after: '142kb',
note: 'Compiler adiciona pouco overhead'
},
developerExperience: {
before: 'Muitos bugs de dependências esquecidas',
after: 'Zero preocupação com memoização',
timesSaved: 'Significativo'
},
runtime: {
before: 'Performance inconsistente',
after: 'Otimização consistente automática',
improvement: '10-30% em apps com memoização incorreta'
},
codeReadability: {
before: 'Poluído com hooks de memoização',
after: 'Limpo e direto ao ponto',
improvement: 'Significativa'
}
};Conclusão
O React Compiler representa uma mudança de paradigma: de "como otimizo isso?" para "escreva código natural e deixe o compiler otimizar".
Principais takeaways:
- Memoização manual é coisa do passado - O compiler faz melhor que humanos
- Código mais limpo - Foque na lógica, não na otimização
- Menos bugs - Sem erros de dependências esquecidas
- Performance consistente - Otimização automática e inteligente
Se você ainda não habilitou o React Compiler no seu projeto, 2026 é o ano de fazer isso. A comunidade React já adotou em massa, e os frameworks principais têm suporte first-class.
Para entender mais sobre o ecossistema JavaScript atual, confira: TypeScript É o Padrão em 2026.

