React Compiler en 2026 : La Fin du useMemo et useCallback Manuel
Salut HaWkers, l'un des plus grands changements dans l'écosystème React en 2026 est l'adoption massive du React Compiler. La mémoïsation manuelle qui tourmentait les développeurs depuis des années devient enfin chose du passé.
Voyons ce qui a changé et comment écrire du React moderne.
Le Problème Que le Compiler Résout
La Douleur de la Mémoïsation Manuelle
Pendant des années, les développeurs React ont dû gérer ceci :
// L'enfer de la mémoïsation manuelle (comme c'était avant)
import { useMemo, useCallback, memo } from 'react';
interface User {
id: number;
name: string;
email: string;
}
interface UserListProps {
users: User[];
onSelect: (user: User) => void;
filter: string;
}
// Composant mémoïsé
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 pour filtrer les users
const filteredUsers = useMemo(() => {
return users.filter(u =>
u.name.toLowerCase().includes(filter.toLowerCase())
);
}, [users, filter]);
// useCallback pour stabiliser la référence
const handleSelect = useCallback((user: User) => {
console.log('Selected:', user);
onSelect(user);
}, [onSelect]);
// useMemo pour calcul coûteux
const stats = useMemo(() => ({
total: users.length,
filtered: filteredUsers.length,
percentage: (filteredUsers.length / users.length * 100).toFixed(1)
}), [users.length, filteredUsers.length]);
return (
<div>
<p>Affichage de {stats.filtered} sur {stats.total} ({stats.percentage}%)</p>
{filteredUsers.map(user => (
<UserCard
key={user.id}
user={user}
onSelect={handleSelect}
/>
))}
</div>
);
}Problèmes de cette approche :
- Code verbeux et difficile à lire
- Facile d'oublier les dépendances
- Sur-mémoïsation (mémoïser des choses qui n'en ont pas besoin)
- Sous-mémoïsation (oublier de mémoïser ce qui en a besoin)
- Difficile de savoir quand mémoïser
Le React Compiler
Comment Ça Fonctionne
// Avec React Compiler (2026) - LE MÊME code, sans mémoïsation manuelle
interface User {
id: number;
name: string;
email: string;
}
interface UserListProps {
users: User[];
onSelect: (user: User) => void;
filter: string;
}
// Sans memo() - le compiler optimise automatiquement
function UserCard({ user, onSelect }: {
user: User;
onSelect: (user: User) => void;
}) {
return (
<div onClick={() => onSelect(user)}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
}
// Code propre et naturel
function UserList({ users, onSelect, filter }: UserListProps) {
// Sans useMemo - le compiler détecte et optimise
const filteredUsers = users.filter(u =>
u.name.toLowerCase().includes(filter.toLowerCase())
);
// Sans useCallback - le compiler stabilise automatiquement
const handleSelect = (user: User) => {
console.log('Selected:', user);
onSelect(user);
};
// Sans useMemo - le compiler sait que c'est un calcul dérivé
const stats = {
total: users.length,
filtered: filteredUsers.length,
percentage: (filteredUsers.length / users.length * 100).toFixed(1)
};
return (
<div>
<p>Affichage de {stats.filtered} sur {stats.total} ({stats.percentage}%)</p>
{filteredUsers.map(user => (
<UserCard
key={user.id}
user={user}
onSelect={handleSelect}
/>
))}
</div>
);
}Ce que fait le compiler :
- Analyse le code au moment de la compilation
- Détecte les valeurs qui peuvent être mémoïsées
- Insère automatiquement la mémoïsation où c'est nécessaire
- Garantit que les callbacks sont stables
- Optimise automatiquement les re-renders
Ce Que le Compiler Analyse
Détection Intelligente
// Le compiler comprend les patterns courants
function ProductPage({ productId }: { productId: string }) {
const [quantity, setQuantity] = useState(1);
// Compiler détecte : dépend uniquement de productId
// Mémoïse automatiquement
const product = products.find(p => p.id === productId);
// Compiler détecte : dépend de product et quantity
// Mémoïse et recalcule quand nécessaire
const totalPrice = product ? product.price * quantity : 0;
// Compiler détecte : fonction passée en prop
// Stabilise automatiquement la référence
const handleAddToCart = () => {
addToCart(productId, quantity);
};
// Compiler détecte : objet créé pendant le render
// Mémoïse pour éviter les re-renders des enfants
const cartItem = {
productId,
quantity,
price: totalPrice
};
return (
<div>
<h1>{product?.name}</h1>
<QuantitySelector
value={quantity}
onChange={setQuantity} // setter est stable par nature
/>
<p>Total : ${totalPrice}</p>
<AddToCartButton
item={cartItem} // objet mémoïsé
onAdd={handleAddToCart} // callback stabilisé
/>
</div>
);
}
Configuration en 2026
Setup Standard
// next.config.ts (Next.js 15+)
import type { NextConfig } from 'next';
const config: NextConfig = {
experimental: {
// React Compiler activé par défaut en 2026
reactCompiler: true,
},
};
export default config;
// Pour les projets 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],
},
}),
],
});Plugin ESLint
// eslint.config.js
import reactCompiler from 'eslint-plugin-react-compiler';
export default [
{
plugins: {
'react-compiler': reactCompiler,
},
rules: {
// Avertit quand le code peut être problématique
'react-compiler/react-compiler': 'error',
},
},
];
Quand Utiliser Encore les Hooks Manuels
Cas Spéciaux
// Cas où la mémoïsation manuelle a encore du sens
// 1. Calculs TRÈS coûteux avec contrôle fin
function DataVisualization({ data }: { data: number[] }) {
// Pour des calculs qui prennent des secondes, vous pourriez
// vouloir un contrôle explicite
const processedData = useMemo(() => {
return expensiveStatisticalAnalysis(data);
}, [data]);
// Ou utiliser la nouvelle API useDeferredValue pour ne pas bloquer
const deferredData = useDeferredValue(data);
return <Chart data={processedData} />;
}
// 2. Intégration avec des bibliothèques externes
function MapComponent({ markers }: { markers: Marker[] }) {
// Les bibliothèques externes peuvent avoir leurs propres
// règles de comparaison
const memoizedMarkers = useMemo(
() => markers.map(m => createMapMarker(m)),
[markers]
);
return <ExternalMapLibrary markers={memoizedMarkers} />;
}
// 3. Refs et imperative handles
function VideoPlayer({ src }: { src: string }) {
const videoRef = useRef<HTMLVideoElement>(null);
// useCallback toujours utile pour les APIs impératives
const play = useCallback(() => {
videoRef.current?.play();
}, []);
const pause = useCallback(() => {
videoRef.current?.pause();
}, []);
// Exposition des méthodes impératives
useImperativeHandle(ref, () => ({
play,
pause,
}), [play, pause]);
return <video ref={videoRef} src={src} />;
}
Patterns Modernes en 2026
Server Components + Compiler
// Combinaison de React Server Components avec le Compiler
// app/products/page.tsx (Server Component)
async function ProductsPage() {
// Données récupérées côté serveur
const products = await fetchProducts();
return (
<div>
<h1>Produits</h1>
{/* Client Component reçoit les données sérialisées */}
<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');
// Le compiler optimise tout automatiquement
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 et Mutations
// Pattern moderne avec 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);
// Le compiler optimise automatiquement
const handleClick = () => {
startTransition(async () => {
await addToCart(productId, quantity);
});
};
return (
<button onClick={handleClick} disabled={isPending}>
{isPending ? 'Ajout en cours...' : 'Ajouter au Panier'}
</button>
);
}
Migration du Code Legacy
Stratégie de Migration
// Étape 1 : Activez le compiler dans le projet
// Étape 2 : Supprimez graduellement la mémoïsation inutile
// AVANT
const MemoizedComponent = memo(function Component({ data }) {
const processed = useMemo(() => process(data), [data]);
const handler = useCallback(() => doSomething(), []);
return <div onClick={handler}>{processed}</div>;
});
// APRÈS
function Component({ data }) {
const processed = process(data);
const handler = () => doSomething();
return <div onClick={handler}>{processed}</div>;
}
// Étape 3 : Faites confiance au plugin ESLint pour signaler les problèmes
// Étape 4 : Testez la performance avant et aprèsQuoi Supprimer
// Checklist de migration
const migrationChecklist = {
// Peut être supprimé en toute sécurité
safeToRemove: [
'React.memo() sur les composants simples',
'useMemo() pour les objets passés en props',
'useCallback() pour les handlers d\'événements',
'useMemo() pour les calculs dérivés simples'
],
// À évaluer au cas par cas
evaluate: [
'useMemo() pour les calculs très coûteux',
'useCallback() dans les bibliothèques externes',
'memo() avec fonction de comparaison personnalisée'
],
// À garder
keep: [
'useRef() - ce n\'est pas de la mémoïsation',
'useState() - ce n\'est pas de la mémoïsation',
'useEffect() - effets de bord',
'useLayoutEffect() - effets de bord synchrones'
]
};
Performance en 2026
Comparatif
// Résultats typiques de migration
const performanceComparison = {
bundleSize: {
before: '145kb',
after: '142kb',
note: 'Le compiler ajoute peu d\'overhead'
},
developerExperience: {
before: 'Nombreux bugs de dépendances oubliées',
after: 'Zéro souci de mémoïsation',
timesSaved: 'Significatif'
},
runtime: {
before: 'Performance inconsistante',
after: 'Optimisation automatique consistante',
improvement: '10-30% dans les apps avec mémoïsation incorrecte'
},
codeReadability: {
before: 'Pollué par les hooks de mémoïsation',
after: 'Propre et direct',
improvement: 'Significative'
}
};Conclusion
Le React Compiler représente un changement de paradigme : de "comment optimiser ceci ?" à "écrivez du code naturel et laissez le compiler optimiser".
Points clés à retenir :
- La mémoïsation manuelle appartient au passé - Le compiler fait mieux que les humains
- Code plus propre - Concentrez-vous sur la logique, pas sur l'optimisation
- Moins de bugs - Plus d'erreurs de dépendances oubliées
- Performance consistante - Optimisation automatique et intelligente
Si vous n'avez pas encore activé le React Compiler dans votre projet, 2026 est l'année pour le faire. La communauté React l'a déjà adopté massivement, et les principaux frameworks ont un support first-class.
Pour en savoir plus sur l'écosystème JavaScript actuel, consultez : TypeScript Est le Standard en 2026.

