Retour au blog

React 19.2 : Activity Component et useEffectEvent Révolutionnent le Développement

Salut HaWkers, React continue d'évoluer et la version 19.2, lancée en octobre 2025, apporte deux nouveautés qui promettent de changer la façon dont nous construisons des applications : le Activity component et le hook useEffectEvent. Ces ajouts résolvent des problèmes que les développeurs affrontent depuis des années.

Avez-vous déjà eu des difficultés à gérer le préchargement de contenu ou vous êtes-vous frustré avec les complexités du tableau de dépendances de useEffect ? Alors cet article est pour vous.

Ce Qui est Nouveau dans React 19.2

C'est la troisième release significative de l'année, suivant React 19 en décembre 2024 et React 19.1 en juin 2025. L'équipe React s'est concentrée sur deux problèmes fondamentaux de l'expérience développeur.

Activity Component : L'Avenir du Pre-Rendering

Le nouveau composant <Activity /> permet de diviser votre application en "activités" qui peuvent être contrôlées et priorisées. C'est une alternative au rendu conditionnel traditionnel, offrant deux modes : visible et hidden.

Pourquoi Activity Est Révolutionnaire

Traditionnellement, quand vous devez rendre du contenu conditionnellement, vous utilisez quelque chose comme ceci :

// Approche traditionnelle - le composant est détruit et recréé
function Dashboard() {
  const [activeTab, setActiveTab] = useState('overview');

  return (
    <div>
      <nav>
        <button onClick={() => setActiveTab('overview')}>Overview</button>
        <button onClick={() => setActiveTab('analytics')}>Analytics</button>
        <button onClick={() => setActiveTab('settings')}>Settings</button>
      </nav>

      {/* Chaque changement d'onglet détruit le composant précédent */}
      {activeTab === 'overview' && <OverviewPanel />}
      {activeTab === 'analytics' && <AnalyticsPanel />}
      {activeTab === 'settings' && <SettingsPanel />}
    </div>
  );
}

Le problème avec cette approche est que chaque changement d'onglet détruit le composant précédent et en crée un nouveau, perdant tout l'état interne et exigeant de nouvelles requêtes de données.

La Solution avec Activity

Avec le Activity component, vous pouvez maintenir les composants montés mais cachés :

import { Activity } from 'react';

function Dashboard() {
  const [activeTab, setActiveTab] = useState('overview');

  return (
    <div>
      <nav>
        <button onClick={() => setActiveTab('overview')}>Overview</button>
        <button onClick={() => setActiveTab('analytics')}>Analytics</button>
        <button onClick={() => setActiveTab('settings')}>Settings</button>
      </nav>

      {/* Les composants restent montés, seule la visibilité change */}
      <Activity mode={activeTab === 'overview' ? 'visible' : 'hidden'}>
        <OverviewPanel />
      </Activity>

      <Activity mode={activeTab === 'analytics' ? 'visible' : 'hidden'}>
        <AnalyticsPanel />
      </Activity>

      <Activity mode={activeTab === 'settings' ? 'visible' : 'hidden'}>
        <SettingsPanel />
      </Activity>
    </div>
  );
}

Avantages du Activity Component

1. Préservation de l'État
Les inputs remplis, la position de scroll et l'état interne sont maintenus quand vous naviguez entre les activities.

2. Pre-fetching Intelligent
Vous pouvez rendre des parties cachées de l'application que l'utilisateur accèdera probablement :

function ProductPage({ productId }) {
  const [showReviews, setShowReviews] = useState(false);

  return (
    <div>
      <ProductDetails id={productId} />

      <button onClick={() => setShowReviews(true)}>
        Voir les Avis
      </button>

      {/* Les avis sont chargés en arrière-plan */}
      <Activity mode={showReviews ? 'visible' : 'hidden'}>
        <ReviewsSection productId={productId} />
      </Activity>
    </div>
  );
}

3. Navigation Instantanée
Les navigations arrière deviennent instantanées car l'état précédent existe toujours en mémoire.

useEffectEvent : Adieu Dependency Hell

Le hook useEffectEvent résout l'un des plus grands maux de tête de React : gérer les dépendances de useEffect quand vous avez besoin de valeurs à jour sans re-exécuter l'effet.

Le Problème Classique

Considérez ce scénario courant :

// Problème : analytics se déclenche plusieurs fois inutilement
function ProductPage({ productId, category, user }) {
  useEffect(() => {
    // Nous voulons logger seulement quand productId change
    // Mais nous avons besoin de category et user.id pour le log
    analytics.logProductView({
      productId,
      category,
      userId: user.id,
      timestamp: Date.now()
    });
  }, [productId, category, user.id]); // Problème : se déclenche si category ou user change

  return <ProductDetails id={productId} />;
}

Solutions Anciennes (Problématiques)

Les développeurs utilisaient des ref pour contourner cela :

// Solution ancienne avec refs - fonctionne mais verbeuse
function ProductPage({ productId, category, user }) {
  const categoryRef = useRef(category);
  const userRef = useRef(user);

  useLayoutEffect(() => {
    categoryRef.current = category;
    userRef.current = user;
  });

  useEffect(() => {
    analytics.logProductView({
      productId,
      category: categoryRef.current,
      userId: userRef.current.id,
      timestamp: Date.now()
    });
  }, [productId]);

  return <ProductDetails id={productId} />;
}

La Solution avec useEffectEvent

Le nouveau hook rend cela élégant et déclaratif :

import { useEffect, useEffectEvent } from 'react';

function ProductPage({ productId, category, user }) {
  // Crée une fonction stable qui accède toujours aux valeurs actualisées
  const logProductView = useEffectEvent(() => {
    analytics.logProductView({
      productId,
      category,     // Toujours la valeur la plus récente
      userId: user.id,  // Toujours la valeur la plus récente
      timestamp: Date.now()
    });
  });

  useEffect(() => {
    // logProductView n'a pas besoin d'être dans les dépendances
    // mais utilise toujours les valeurs les plus récentes des props
    logProductView();
  }, [productId]); // Se déclenche seulement quand productId change

  return <ProductDetails id={productId} />;
}

Cas d'Usage Pratiques de useEffectEvent

1. Event Handlers dans les Effects

function ChatRoom({ roomId, onMessage }) {
  // onMessage peut changer, mais nous ne voulons pas reconnecter
  const handleMessage = useEffectEvent((message) => {
    onMessage(message);
  });

  useEffect(() => {
    const connection = createConnection(roomId);

    connection.on('message', handleMessage);
    connection.connect();

    return () => connection.disconnect();
  }, [roomId]); // Reconnecte seulement quand roomId change
}

2. Logging et Analytics

function SearchResults({ query, filters, results }) {
  const logSearch = useEffectEvent(() => {
    analytics.logSearch({
      query,
      filterCount: Object.keys(filters).length,
      resultCount: results.length,
      timestamp: Date.now()
    });
  });

  useEffect(() => {
    // Logue seulement quand query change
    // mais inclut les données actualisées de filters et results
    logSearch();
  }, [query]);

  return <ResultsList results={results} />;
}

3. Synchronisation avec des APIs Externes

function VideoPlayer({ videoId, volume, playbackRate }) {
  const updatePlayerSettings = useEffectEvent(() => {
    // Configurations qui ne doivent pas causer le rechargement de la vidéo
    player.setVolume(volume);
    player.setPlaybackRate(playbackRate);
  });

  useEffect(() => {
    const player = createPlayer(videoId);

    // Applique les configurations initiales
    updatePlayerSettings();

    return () => player.destroy();
  }, [videoId]); // Recharge la vidéo seulement quand videoId change

  // Met à jour les configurations sans recharger
  useEffect(() => {
    updatePlayerSettings();
  }, [volume, playbackRate]);
}

React Performance Tracks dans DevTools

React 19.2 ajoute aussi de nouvelles tracks de performance dans Chrome DevTools qui facilitent beaucoup le debugging.

Scheduler Track

Montre ce que React traite à différentes priorités :

function App() {
  const [searchTerm, setSearchTerm] = useState('');
  const [results, setResults] = useState([]);

  const handleSearch = (term) => {
    // Priorité haute - mise à jour immédiate de l'input
    setSearchTerm(term);

    // Priorité basse - la recherche ne bloque pas l'UI
    startTransition(() => {
      const filtered = performExpensiveSearch(term);
      setResults(filtered);
    });
  };

  return (
    <div>
      <input
        value={searchTerm}
        onChange={(e) => handleSearch(e.target.value)}
      />
      <SearchResults results={results} />
    </div>
  );
}

Dans DevTools, vous verrez des tracks séparées pour :

  • Blocking : Interactions utilisateur (haute priorité)
  • Transition : Updates dans startTransition
  • Idle : Travail de basse priorité

Migration et Compatibilité

Mise à Jour vers React 19.2

La mise à jour est simple et n'a pas de breaking changes par rapport à React 19.1 :

# npm
npm install react@19.2.0 react-dom@19.2.0

# yarn
yarn add react@19.2.0 react-dom@19.2.0

# pnpm
pnpm add react@19.2.0 react-dom@19.2.0

Compatibilité des Types TypeScript

Pour TypeScript, assurez-vous de mettre à jour les types :

npm install @types/react@19.2.0 @types/react-dom@19.2.0

Les nouveaux types incluent des définitions complètes pour Activity et useEffectEvent :

import { Activity, useEffectEvent } from 'react';

interface ActivityProps {
  mode: 'visible' | 'hidden';
  children: React.ReactNode;
}

// useEffectEvent préserve le type de la fonction
const handler = useEffectEvent((event: MouseEvent) => {
  // event est correctement typé
  console.log(event.clientX, event.clientY);
});

Comparaison avec les Approches Précédentes

Activity vs CSS display: none

Aspect Activity hidden CSS display: none
React Lifecycle Pausé Continue d'exécuter
Mémoire Optimisée État maintenu
Effects N'exécutent pas Continuent d'exécuter
Re-render Ne se produit pas Peut se produire

useEffectEvent vs useCallback

Aspect useEffectEvent useCallback
Valeurs Toujours à jour Périmées si deps changent
Dépendances Jamais listé Doit être listé
Cas d'usage Effects Props de composants
Stabilité Toujours stable Dépend des deps

Conclusion

React 19.2 représente un pas important supplémentaire dans l'évolution du framework. L'Activity component résout élégamment le problème du préchargement et de la préservation d'état, tandis que useEffectEvent élimine des années de frustration avec les dependency arrays.

Ces nouveaux outils ne sont pas juste des améliorations incrémentales - ils changent fondamentalement la façon dont nous pensons à la gestion d'état et aux effets secondaires dans les applications React.

Si vous travaillez avec React, je recommande de commencer à expérimenter ces features dans des projets plus petits pour comprendre leur potentiel. Pour compléter vos connaissances sur la performance, consultez notre article sur ECMAScript 2025 et les Nouvelles Features du JavaScript.

C'est parti ! 🦅

Commentaires (0)

Cet article n'a pas encore de commentaires. Soyez le premier!

Ajouter des commentaires