P PasteCode
Échec

Comment corriger la surutilisation de useEffect par l'IA

Les agents IA utilisent useEffect pour gérer l'état dérivé, les transformations d'événements et la récupération de données qui devraient plutôt utiliser useMemo, les gestionnaires d'événements ou les Server Components.

CursorClaude CodeCodexWindsurf Next.jsTypeScript
.md .json Mis à jour 8 juin 2026

L’agent encapsule la logique dans useEffect qui devrait être un état dérivé, un gestionnaire d’événements ou une récupération par Server Component — provoquant des rendus supplémentaires, des fermetures obsolètes et des conditions de concurrence.

Le symptôme

useEffect est utilisé pour calculer des valeurs dérivées ou synchroniser un état qui pourrait être un simple calcul.

"use client";
import { useState, useEffect } from "react";
// WRONG — useEffect to compute derived state
export function CartSummary({ items }: { items: CartItem[] }) {
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(items.reduce((sum, item) => sum + item.price * item.qty, 0));
}, [items]);
return <p>Total: ${total}</p>;
// Renders twice: once with total=0, once after the effect runs
}

Un deuxième modèle : récupérer des données dans useEffect alors qu’un Server Component pourrait le faire sans aucun coût de bundle client.

"use client";
useEffect(() => {
fetch("/api/products").then((r) => r.json()).then(setProducts);
}, []);
// Waterfall: page loads -> JS executes -> fetch starts -> render

Pourquoi cela se produit

useEffect était la principale échappatoire pour les effets de bord dans React 16/17. Les modèles entraînés sur du code pré-React-18 et pré-App-Router ont appris à l’utiliser par défaut. Le modèle mental “J’ai besoin que quelque chose se produise après le rendu” correspond à useEffect même lorsque la vraie solution est plus simple.

Comment le repérer

  • useEffect définit un état qui est directement calculable à partir des props ou d’un autre état.
  • useEffect(() => { fetch(...).then(setState) }, []) en haut d’un composant qui n’est pas dans un arbre Server Component.
  • useEffect sans nettoyage dont la dépendance est une fonction (risque de fermeture obsolète).
  • useEffect utilisé uniquement pour la journalisation ou l’analyse lors du montage.

Comment le corriger

Utilisez le bon outil pour la tâche :

// CORRECT — derived state: just compute it
export function CartSummary({ items }: { items: CartItem[] }) {
const total = items.reduce((sum, item) => sum + item.price * item.qty, 0);
return <p>Total: ${total.toFixed(2)}</p>;
}
// CORRECT — expensive derivation: useMemo
import { useMemo } from "react";
export function FilteredList({ items, query }: { items: Item[]; query: string }) {
const filtered = useMemo(
() => items.filter((i) => i.name.toLowerCase().includes(query.toLowerCase())),
[items, query]
);
return <ul>{filtered.map((i) => <li key={i.id}>{i.name}</li>)}</ul>;
}
app/products/page.tsx
// CORRECT — data fetching: async Server Component (no useEffect needed)
export default async function ProductsPage() {
const products = await fetch("https://api.example.com/products").then((r) =>
r.json()
);
return <ProductList products={products} />;
}
[ ] Derived values from props/state are plain calculations, not useEffect + setState
[ ] Expensive derivations use useMemo, not useEffect
[ ] Data fetching on mount moves to async Server Components or React Query/SWR
[ ] Event-driven side-effects (form submit, button click) are in event handlers
[ ] useEffect is reserved for: external system sync, subscriptions, cleanup
[ ] Every useEffect that sets state has a loading/error state and cleanup

Prompt de correction

Fix Prompt
This component uses useEffect to compute derived state or fetch initial data.
Refactor it: replace derived-state effects with direct calculations or useMemo,
move initial data fetching to an async Server Component parent or to a
data-fetching library (SWR/React Query) with proper loading/error handling, and
keep useEffect only for genuine external-system synchronization that requires
cleanup. Explain each useEffect you keep and why it cannot be replaced.

Test

Terminal window
# Count useEffect calls — a high number is a smell worth reviewing
grep -rn "useEffect" --include="*.tsx" --include="*.ts" src/ app/ \
| grep -v "node_modules" \
| wc -l
# Flag useEffect+setState patterns for manual review
grep -A5 "useEffect" app/**/*.tsx 2>/dev/null | grep "setState\|set[A-Z]" \
&& echo "REVIEW: possible derived-state antipattern" || echo "OK"
Étiquettes: reactNext.jshydration