Le guide des re-render en React JS

Le guide des re-render en React JS

  React Typescript

Introduction

React est une bibliothèque JavaScript populaire utilisée pour la construction d’interfaces utilisateur. L’un des avantages clés de React est sa capacité à mettre à jour de manière efficace l’interface utilisateur en réponse à des changements d’état. Cependant, il peut être parfois difficile de comprendre exactement quand et comment un composant est re-rendu. Dans ce tutoriel, nous allons examiner de plus près comment React gère les re-render des composants.

🎨 Qu’est-ce qu’un re-render ?

Lorsque l’on parle de performances de React, il y a deux étapes majeures dont nous devons nous soucier :

Le re-rendu se produit lorsque React doit mettre à jour l’application avec de nouvelles données. Habituellement, cela se produit à la suite d’une interaction d’un utilisateur avec l’application ou de certaines données externes via une demande asynchrone ou un modèle d’abonnement.

🧐 Comment se déclenche le re-render ?

Il y a quatre raisons pour lesquelles un composant se re-render de lui-même : les changements d’état, les re-rendus parent (ou enfants), les changements de Context et les changements des Hooks.

Il existe également un grand mythe : les rendus se produisent lorsque les Props du composant changent. En soi, ce n’est pas vrai !

🔋 Le changement d’état

Lorsque l’état d’un composant change, il se re-render de lui-même. Habituellement, cela se produit soit dans un callback, soit dans le hook useEffect.

Les changements d’état sont la source principale de tous les re-rendus.

import { FC, useState } from "react";

export const MyComponent: FC = () => {
  // when setLabel function is called, the MyComponent re-render itself
  const [label, setLabel] = useState("");

  return <div>{label}</div>;
};

🧔🏻‍♂️ Le parent se re-render

Un composant s’affichera à nouveau si son parent s’affiche à nouveau. Ou, si nous regardons cela dans la direction opposée : lorsqu’un composant se re-render, il re-render également tous ses enfants.

Le re-render va toujours vers le bas de l’arborescence : le re-rendu d’un enfant ne déclenche pas le re-rendu d’un parent.

import { FC } from "react";

export const Parent: FC = () => {
  // if Parent component is re-rendered, the Child component will also be re-renderer
  return <Child />;
};

🔍 Utilisation des Contexts

Lorsque la valeur stockée dans un Context React change, tous les composants qui utilisent ce même Context seront re-rendus, même s’ils n’utilisent pas directement la partie modifiée des données. Ces re-rendus ne peuvent pas être empêchés avec la mémorisation directement, mais il existe quelques solutions de contournement qui peuvent le simuler.

// context/drawer-context.tsx
import { createContext, PropsWithChildren, useState } from "react";

interface AppContextInterface {
  isDrawerOpen: boolean;
}

const AppContext = createContext<AppContextInterface>({
  isDrawerOpen: false,
});

export const AppContextProvider: PropsWithChildren = ({ children }) => {
  const [isDrawerOpen, setIsDrawerOpen] = useState(false);

  return (
    <AppContext.Provider value={{ isDrawerOpen }}>
      {children}
    </AppContext.Provider>
  );
};

export default AppContext;

// components/Component1.tsx
import { FC, useContext, useState } from "react";

export const Component1: FC = () => {
  // when setIsDrawerOpen from context is called, the Component1 re-render itself
  const { setIsDrawerOpen } = useContext(AppContext);

  function onClickHandler(): void {
    setIsDrawerOpen(true);
  }

  return <button onClick={onClickHandler}>Open Drawer</button>;
};

🔗 Changement d’état d’un custom hook

Tout ce qui se passe à l’intérieur d’un Hook affecte le composant qui l’utilise. Les mêmes règles concernant les changements de Context et d’état s’appliquent ici :

// hooks/use-form-input.ts
import { useState, FormEvent } from "react";

export function useFormInput(initialValue: string) {
  const [value, setValue] = useState(initialValue);

  function handleChange(event: FormEvent<HTMLInputElement>) {
    setValue(event.target.value);
  }

  return {
    value,
    onChange: handleChange,
  };
}

//components/Component2.tsx
import { Fragment, FC } from "react";
import { useFormInput } from "../../hooks/use-form-input.ts";

export const Component2: FC = () => {
  // when handleChange from custom hook is called, the Component2 re-render itself
  const firstNameProps = useFormInput("Mary");

  return (
    <Fragment>
      <label>FirstName</label>
      <input {...firstNameProps} />
    </Fragment>
  );
};

🤥 Le faux mythe : le changement des Props

Peu importe que les Props du composant changent ou non lorsqu’il s’agit de re-render des composants non mémorisés.

Pour que les Props changent, elles doivent être mis à jour par le composant parent. Cela signifie que le parent devra effectuer un nouveau rendu, ce qui déclenchera un nouveau rendu du composant enfant, quels que soient ses Props.

Ce n’est que lorsque des techniques de mémorisation sont utilisées comme React.memo ou useMemo que le changement des Props aura de l’importance.

import { FC } from "react";

export const Parent: FC = () => {
  let value = "my value";

  useEffect(() => {
    value = "my value2";
  }, []);

  // if value changed, the Parent and Child will not be re-rendered.
  // if Parent component is re-rendered, the Child component will also be re-renderer
  return <Child value={value} />;
};