Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
// Passo 1: Instale o compilador
// npm install -D babel-plugin-react-compiler
// Passo 2: Adicione à sua configuração do Next.js (next.config.ts)
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
export default nextConfig;
// Passo 3: Remova a memoização manual - o compilador cuida disso
// ANTES: Memoização manual
function ProductList({ products, category }: Props) {
const filtered = useMemo(
() => products.filter((p) => p.category === category),
[products, category]
);
const handleClick = useCallback((id: string) => {
selectProduct(id);
}, [selectProduct]);
return <MemoizedGrid items={filtered} onClick={handleClick} />;
}
const MemoizedGrid = memo(Grid);
// DEPOIS: O compilador auto-memoiza - código mais limpo, mesmo desempenho
function ProductList({ products, category }: Props) {
const filtered = products.filter((p) => p.category === category);
const handleClick = (id: string) => selectProduct(id);
return <Grid items={filtered} onClick={handleClick} />;
}Quando usar isso: Ao iniciar um novo projeto React 19+ ou migrar um existente. O compilador elimina o código boilerplate de useMemo, useCallback e memo, mantendo um desempenho igual ou melhor.
// ---- ANTES: Memoização manual com 15 linhas de boilerplate ----
import { memo, useMemo, useCallback, useState } from "react";
interface User {
id: string;
name: string;
role: "admin" | "user";
lastActive: Date;
}
function UserDashboard({ users }: { users: User[] }) {
const [search, setSearch] = useState("");
const [roleFilter, setRoleFilter] = useState<"all" | "admin" | "user">("all");
const [selectedId, setSelectedId] = useState<string | null>(null);
const filteredUsers = useMemo(
() =>
users
.filter((u) => roleFilter === "all" || u.role === roleFilter)
.filter((u) => u.name.toLowerCase().includes(search.toLowerCase()))
.sort((a, b) => b.lastActive.getTime() - a.lastActive.getTime()),
[users, roleFilter, search]
);
const handleSelect = useCallback((id: string) => {
setSelectedId(id);
}, []);
const stats = useMemo(
() => ({
total: users.length,
admins: users.filter((u) => u.role === "admin").length,
active: users.filter((u) => {
const hourAgo = Date.now() - 3600_000;
return u.lastActive.getTime() > hourAgo;
}).length,
}),
[users]
);
return (
<div>
<MemoizedStatsBar stats={stats} />
<input value={search} onChange={(e) => setSearch(e.target.value)} />
<select
value={roleFilter}
onChange={(e) => setRoleFilter(e.target.value as "all" | "admin" | "user")}
>
<option value="all">All</option>
<option value="admin">Admin</option>
<option value="user">User</option>
</select>
<MemoizedUserList
users={filteredUsers}
selectedId={selectedId}
onSelect={handleSelect}
/>
</div>
);
}
const MemoizedStatsBar = memo(function StatsBar({
stats,
}: {
stats: { total: number; admins: number; active: number };
}) {
return (
<div className="flex gap-4">
<span>Total: {stats.total}</span>
<span>Admins: {stats.admins}</span>
<span>Active: {stats.active}</span>
</div>
);
});
const MemoizedUserList = memo(function UserList({
users,
selectedId,
onSelect,
}: {
users: User[];
selectedId: string | null;
onSelect: (id: string) => void;
}) {
return (
<ul>
{users.map((user) => (
<li
key={user.id}
onClick={() => onSelect(user.id)}
className={user.id === selectedId ? "bg-blue-100" : ""}
>
{user.name} ({user.role})
</li>
))}
</ul>
);
});
// ---- DEPOIS: React Compiler - mesmo desempenho, zero boilerplate ----
function UserDashboard({ users }: { users: User[] }) {
const [search, setSearch] = useState("");
const [roleFilter, setRoleFilter] = useState<"all" | "admin" | "user">("all");
const [selectedId, setSelectedId] = useState<string | null>(null);
const filteredUsers = users
.filter((u) => roleFilter === "all" || u.role === roleFilter)
.filter((u) => u.name.toLowerCase().includes(search.toLowerCase()))
.sort((a, b) => b.lastActive.getTime() - a.lastActive.getTime());
const stats = {
total: users.length,
admins: users.filter((u) => u.role === "admin").length,
active: users.filter((u) => {
const hourAgo = Date.now() - 3600_000;
return u.lastActive.getTime() > hourAgo;
}).length,
};
return (
<div>
<StatsBar stats={stats} />
<input value={search} onChange={(e) => setSearch(e.target.value)} />
<select
value={roleFilter}
onChange={(e) => setRoleFilter(e.target.value as "all" | "admin" | "user")}
>
<option value="all">All</option>
<option value="admin">Admin</option>
<option value="user">User</option>
</select>
<UserList
users={filteredUsers}
selectedId={selectedId}
onSelect={(id: string) => setSelectedId(id)}
/>
</div>
);
}
function StatsBar({ stats }: { stats: { total: number; admins: number; active: number } }) {
return (
<div className="flex gap-4">
<span>Total: {stats.total}</span>
<span>Admins: {stats.admins}</span>
<span>Active: {stats.active}</span>
</div>
);
}
function UserList({
users,
selectedId,
onSelect,
}: {
users: User[];
selectedId: string | null;
onSelect: (id: string) => void;
}) {
return (
<ul>
{users.map((user) => (
<li
key={user.id}
onClick={() => onSelect(user.id)}
className={user.id === selectedId ? "bg-blue-100" : ""}
>
{user.name} ({user.role})
</li>
))}
</ul>
);
}O que isso demonstra:
useMemo x2, useCallback x1, wrappers memo x2) e 15 linhas de código boilerplate.React.memo.useMemo manual, que armazena em cache na granularidade que você especifica, o compilador pode armazenar em cache elementos JSX individuais dentro de um componente, potencialmente alcançando uma granularidade melhor do que a otimização escrita manualmente.Optando por sair com a diretiva "use no memo":
// Opta um componente específico fora da otimização do compilador
function DebugPanel({ data }: { data: unknown }) {
"use no memo";
// Este componente sempre re-renderiza - útil para depuração
console.log("DebugPanel renderizado em", Date.now());
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}Opt-in por arquivo (migração gradual):
// next.config.ts - compila apenas diretórios específicos
const nextConfig = {
experimental: {
reactCompiler: {
compilationMode: "annotation",
// Ou direcione caminhos específicos:
// include: ["src/components/**"],
},
},
};// Opta pelo nível do componente com "use memo"
function ExpensiveComponent({ data }: Props) {
"use memo";
// O compilador otimiza este componente
return <Chart data={data} />;
}Verificando a saída do compilador:
# Veja o que o compilador transforma
npx react-compiler-healthcheck
# Ou inspecione a saída compilada
npx babel --plugins babel-plugin-react-compiler src/Component.tsx"use no memo" é uma string literal e não precisa de uma declaração de tipo.Estado mutável externo quebra as suposições do compilador - O compilador assume renderização pura. Se um componente lê de uma variável global mutável ou de um ref durante a renderização, o compilador pode armazenar em cache um resultado obsoleto. Correção: Use useSyncExternalStore para stores externos ou adicione a diretiva "use no memo".
Efeitos colaterais durante a renderização - Código que registra, muta objetos externos ou dispara requisições de rede durante a renderização será cacheado pelo compilador, o que significa que os efeitos colaterais podem não ser executados a cada renderização como esperado. Correção: Mova os efeitos colaterais para useEffect ou manipuladores de eventos.
Computações não idempotentes - Se o cálculo de um valor depende de Date.now(), Math.random() ou outras fontes não determinísticas, o compilador pode armazenar em cache um resultado obsoleto. Correção: Mova valores não determinísticos para estado ou efeitos.
Componentes de classe não são otimizados - O compilador funciona apenas com componentes de função e hooks. Componentes de classe são passados sem alterações. Correção: Migre componentes de classe para componentes de função.
Memoização manual ainda é necessária em casos raros - O compilador pode não otimizar limites entre componentes em todos os casos, especialmente com componentes de ordem superior ou render props. Correção: Perfis após habilitar o compilador. Adicione memo manual apenas onde o profiling mostrar que o compilador perdeu uma otimização.
Mantenha a memoização manual existente durante a migração - Remover useMemo/useCallback antes de habilitar o compilador deixa seu aplicativo não otimizado. Correção: Habilite o compilador primeiro, verifique se o desempenho é mantido e, em seguida, remova a memoização manual.
| Abordagem | Compromisso |
|---|---|
useMemo manual + useCallback + memo | Controle total; verboso; arrays de dependência propensos a erros |
| React Compiler | Zero boilerplate; requer React 19+; experimental |
| Colocação de estado | Evita a necessidade de memoização; pode precisar de reestruturação |
| Seletores Zustand | Assinaturas granulares; adiciona dependência externa |
| Million.js | Compilador alternativo para otimização de DOM virtual; terceirizado |
| Sinais (Preact, Solid) | Modelo reativo evita o problema de re-renderização; framework diferente |
O React Compiler é um plugin Babel que analisa o código do componente em tempo de build e insere automaticamente a memoização (equivalente a useMemo, useCallback e memo). Diferente da memoização manual, você não escreve arrays de dependência -- o compilador rastreia as dependências estaticamente.
// next.config.ts
const nextConfig = {
experimental: {
reactCompiler: true,
},
};
export default nextConfig;Instale também o plugin Babel: npm install -D babel-plugin-react-compiler.
Sim. Adicione a diretiva "use no memo" no topo do corpo do componente:
function DebugPanel({ data }: { data: unknown }) {
"use no memo";
console.log("Renderizado em", Date.now());
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}Violar essas regras pode produzir resultados cacheados incorretos.
O compilador pode armazenar em cache um resultado obsoleto porque assume renderização pura. Se um componente lê de window.someGlobal ou de um ref mutável durante a renderização, o resultado em cache não refletirá as atualizações desse global.
Correção: Use useSyncExternalStore para stores externos ou adicione "use no memo".
O compilador armazena em cache a saída do componente. Se efeitos colaterais (logs, requisições de rede, mutações) são executados durante a renderização, eles podem ser ignorados quando o resultado em cache é reutilizado.
Correção: Mova os efeitos colaterais para useEffect ou manipuladores de eventos.
Não. O compilador otimiza apenas componentes de função e hooks. Componentes de classe são passados sem alterações. Migre componentes de classe para componentes de função para se beneficiar.
Não. Habilite o compilador primeiro, verifique se o desempenho é mantido e, em seguida, remova a memoização manual. Remover hooks antes de habilitar o compilador deixa seu aplicativo não otimizado durante o intervalo.
Não. O compilador funciona com TypeScript imediatamente. As anotações de tipo são preservadas -- o compilador apenas transforma o comportamento em tempo de execução. Componentes genéricos são suportados sem alterações.
npx react-compiler-healthcheck
# Ou inspecione a saída compilada diretamente:
npx babel --plugins babel-plugin-react-compiler src/Component.tsxNo modo compilationMode: "annotation", apenas os componentes com a diretiva "use memo" são otimizados pelo compilador. Isso permite a migração gradual optando por componente.
O useMemo manual armazena em cache na granularidade que você especifica (um valor por chamada de hook). O compilador pode armazenar em cache elementos JSX individuais dentro de um componente, potencialmente alcançando uma granularidade melhor do que a otimização escrita manualmente.
useMemo, useCallback e React.memoRevisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥