AccueilClients

Applications et sites

  • Application métiersIntranet, back-office...
  • Applications mobilesAndroid & iOS
  • Sites InternetSites marketings et vitrines
  • Expertises techniques

  • React
  • Expo / React Native
  • Next.js
  • Node.js
  • Directus
  • TypeScript
  • Open SourceBlogContactEstimer

    30 septembre 2025

    Activity, le nouveau composant React

    4 minutes de lecture

    Activity, le nouveau composant React
    🇺🇸 This post is also available in english

    Fraichement sorti sur le canal canary de React, le composant Activity est une nouveauté ayant pour but de faciliter la gestion d'affichage conditionnel de composants, permettant de conserver leur état tout en les cachant visuellement. Découvrons ensemble son utilisation.

    Installation

    Créons un simple projet React avec Vite :

    bun create vite@latest
    

    Nous devrons modifier le package.json afin de faire pointer les versions de React et React DOM vers la version canary.

    Cas d'utilisation

    Nous allons réaliser un cas d'utilisation qui peut s'avérer fréquent : la gestion d'un formulaire en plusieurs étapes.

    Utilisation basique

    Actuellement, l'utilisation la plus classique serait la suivante :

    function App() {
      const [step, setStep] = useState(1);
    
    return (
    
    <div
      style={{
        margin: '1rem',
        display: 'flex',
        alignItems: 'center',
        flex: 1,
        flexDirection: 'column',
        gap: '1rem',
      }}
    >
      <div style={{ display: 'flex', gap: '1rem' }}>
        <button type="button" onClick={() => setStep(1)}>
          Step 1
        </button>
        <button type="button" onClick={() => setStep(2)}>
          Step 2
        </button>
      </div>
      {step === 1 && <Step1 />}
      {step === 2 && <Step2 />}
    </div>
    ); }
    
    

    En l'état, ce bout de code est fonctionnel mais présente un inconvénient en terme d'UX : lorsque l'on remplit un champ puis que l'on change d'étape, l'état de ce champ est perdu. C'est logique : notre composant a été démonté. Pour palier à ça, on pourrait tout à fait modifier le code afin de cacher le composant, de sorte qu'il soit toujours présent dans l'arbre React.

    App.tsx
    function App() {
      const [step, setStep] = useState(1);
    
      return (
        <div
          style={{
            margin: "1rem",
            display: "flex",
            alignItems: "center",
            flex: 1,
            flexDirection: "column",
            gap: "1rem",
          }}
        >
          <div style={{ display: "flex", gap: "1rem" }}>
            <button type="button" onClick={() => setStep(1)}>
              Step 1
            </button>
            <button type="button" onClick={() => setStep(2)}>
              Step 2
            </button>
          </div>
          <div style={step === 2 ? { display: "none" } : {}}>
            <Step1 />
          </div>
          <div style={step === 1 ? { display: "none" } : {}}>
            <Step2 />
          </div>
        </div>
      );
    }
    

    C'est fonctionnel mais pas idéal, imaginons que nos composants doivent gérer des événements, ou du contenu interactif comme une vidéo, nous devrions quand même passer une prop afin de gérer leur activation / désactivation. De même, la présence d'un effect (useEffect ou useLayoutEffect) devrait avoir son déclenchement réglé selon cette prop. Bien que cela soit possible, cela rajouterait de la complexité à notre composant.

    Le composant Activity à la rescousse

    C'est dans ce cadre qu'intervient le composant Activity. Son rôle est simple : cacher le noeud du DOM avec un display: none, conserver l'état du noeud React, et n'exécuter les effects uniquement quand notre composant est visible. En quelque sorte, c'est comme si notre composant était partiellement monté et démonté.

    App.tsx
    function App() {
      const [step, setStep] = useState(1);
    
      return (
        <div
          style={{
            margin: "1rem",
            display: "flex",
            alignItems: "center",
            flex: 1,
            flexDirection: "column",
            gap: "1rem",
          }}
        >
          <div style={{ display: "flex", gap: "1rem" }}>
            <button type="button" onClick={() => setStep(1)}>
              Step 1
            </button>
            <button type="button" onClick={() => setStep(2)}>
              Step 2
            </button>
          </div>
          <Activity mode={step === 1 ? "visible" : "hidden"}>
            <Step1 />
          </Activity>
          <Activity mode={step === 2 ? "visible" : "hidden"}>
            <Step2 />
          </Activity>
        </div>
      );
    }
    

    La saisie de texte est maintenant conservée entre chaque changement d'étape. En inspectant le DOM, on peut voir qu'un style display: none est appliqué à notre input caché.

    Pour aller plus loin, on peut observer le comportement des effects, à l'aide de simples console.log.

    step2.tsx
    const Step2 = () => {
      const [address, setAddress] = useState("");
    
      useEffect(() => {
        console.log("Step2 mounted");
    
        return () => {
          console.log("Step2 unmounted");
        };
      }, []);
    
      useLayoutEffect(() => {
        console.log("Step2 layout mounted");
    
        return () => {
          console.log("Step2 layout unmounted");
        };
      }, []);
    
      return (
        <input
          value={address}
          onChange={(e) => setAddress(e.target.value)}
          name="address"
          placeholder="Address"
        />
      );
    };
    

    En rafraichissant la page, on peut voir qu'aucun des logs ne s'affiche. Pourtant, notre input est bien dans le DOM. En cliquant sur notre étape 2, nos effects sont bien déclenchés, et les fonctions de cleanup sont exécutées si l'on revient à l'étape 1.

    Pré-rendu et données distantes

    Afin de récupérer des données distantes, un cas d'utilisation courant serait d'effectuer une requête dans un useEffect. Or on a vu précédemment que les effects ne sont pas exécutés dans le cadre de l'utilisation d'Activity. Pour palier à cela, nous allons utiliser le hook use.

    step2.tsx
    const fetchAddress = new Promise<string>((resolve) => {
      resolve("Some street name");
    });
    
    const Step2 = () => {
      const addressData = use(fetchAddress);
      const [address, setAddress] = useState(addressData);
    
      return (
        <input
          value={address}
          onChange={(e) => setAddress(e.target.value)}
          name="address"
          placeholder="Address"
        />
      );
    };
    

    Maintenant, au chargement de la page, notre promesse fetchAddress est exécutée dès lors que Step2 est instancié, et on retrouve la valeur résolue affichée dans notre input.

    Conclusion

    Ce tout nouveau composant Activity va simplifier certains cas complexes que l'on pouvait rencontrer jusqu'à présent dans nos applications. Son utilisation est très intuitive, mais nécessitera toutefois un peu de refactorisation dans les projets existants, notamment par rapport à la modification du comportement des effects.

    Une idée de projet React, besoin d'accompagnement ? N'hésitez pas à nous contacter !

    Ressources:

    À découvrir également

    Premier Octet vous accompagne dans le développement de vos projets avec react

    Discuter de votre projet react
    18 avenue Parmentier
    75011 Paris
    +33 1 43 57 39 11hello@premieroctet.com
    ContactMentions légales
    Suivez nos aventures

    GitHub

    X

    Flux RSS

    Bluesky

    Navigation
    Nos expertises métiers

    Applications et sites

    E-commerce & SaaS

    Intelligence artificielle