Fermer

juillet 8, 2022

Comment convertir des composants de classe en crochets – Moderniser l’application React


Cet article explique comment convertir les cas d’utilisation courants des composants de classe en hooks, afin que vous puissiez moderniser vos applications React.

React existe depuis de nombreuses années et est souvent choisi comme solution pour créer des interfaces utilisateur dans des applications modernes. Au fil des années, la façon dont nous écrivons des composants avec React a beaucoup changé.

Au départ, nous avions le createClass méthode, qui a ensuite été remplacée par des composants de classe. Dans la version 16.8, React a publié des crochets qui ont révolutionné la façon dont nous écrivons des applications React, car ils nous ont permis d’écrire un code plus concis et plus propre et ont fourni un meilleur modèle pour créer une logique avec état réutilisable.

De nombreux développeurs se sont tournés vers les crochets et les composants de classe abandonnés. Cependant, de nombreuses applications React héritées utilisent toujours des composants de classe. De plus, les composants de classe ont toujours leurs cas d’utilisation, tels que les limites d’erreur, car il n’y a pas de crochet pour cela.

Dans cet article, nous expliquerons comment convertir les cas d’utilisation courants des composants de classe en hooks.

Vous pouvez trouver des exemples de code complets dans ce dépôt GitHub et un CodeSandbox interactif ci-dessous.

Gestion et mise à jour de l’état des composants

Gestion de l’état est l’une des choses les plus courantes dans toute application React. React rend les composants en fonction de l’état et des accessoires. Chaque fois qu’ils changent, les composants sont restitués et le DOM est mis à jour en conséquence. Voici un exemple de composant de classe simple avec un état de compteur et deux méthodes pour le mettre à jour.

import { Component } from "react";

class ManagingStateClass extends Component {
  state = {
    counter: 0,
  };

  increment = () => {
    this.setState(prevState => {
      return {
        counter: prevState.counter + 1,
      };
    });
  };

  decrement = () => {
    this.setState(prevState => {
      return {
        counter: prevState.counter - 1,
      };
    });
  };

  render() {
    return (
      <div>
        <h2>Managing State - Class</h2>
        <div>Count: {this.state.counter}</div>
        <div>
          <button onClick={this.increment}>Increment</button>
          <button onClick={this.decrement}>Decrement</button>
        </div>
      </div>
    );
  }
}

export default ManagingStateClass;

L’implémentation des crochets est beaucoup plus concise.

import { useState } from "react";

const ManagingStateHooks = () => {
  const [counter, setCounter] = useState(0);

  const increment = () => setCounter(counter => counter + 1);
  const decrement = () => setCounter(counter => counter - 1);

  return (
    <div>
      <h2>Managing State - Hooks</h2>
      <div>Count: {counter}</div>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
      </div>
    </div>
  );
};

export default ManagingStateHooks;

Le composant est juste une fonction qui renvoie JSX. Nous utilisons le useState crochet pour gérer l’état. Il renvoie un tableau avec deux valeurs – la première est l’état et la seconde est la fonction de mise à jour. Nous avons également increment et decrement fonctions qui utilisent le setCounter mise à jour.

Réagir aux changements d’état

Il existe des scénarios dans lesquels nous pourrions avoir besoin d’effectuer une sorte d’action chaque fois que l’état change. Dans un composant de classe, nous pouvons le faire en utilisant le componentDidUpdate cycle de la vie.

import { Component } from "react";

class StateChangesClass extends Component {
  state = {
    counter: 0,
  };

  componentDidUpdate(prevProps, prevState) {
    console.log("New counter", this.state.counter);
    localStorage.setItem("counter", this.state.counter);
  }

  increment = () => {
    this.setState(prevState => {
      return {
        counter: prevState.counter + 1,
      };
    });
  };

  decrement = () => {
    this.setState(prevState => {
      return {
        counter: prevState.counter - 1,
      };
    });
  };

  render() {
    return (
      <div>
        <h2>Reacting To State Changes - Class</h2>
        <div>Count: {this.state.counter}</div>
        <div>
          <button onClick={this.increment}>Increment</button>
          <button onClick={this.decrement}>Decrement</button>
        </div>
      </div>
    );
  }
}

export default StateChangesClass;

Lorsque l’état change, nous enregistrons la nouvelle valeur du compteur dans le stockage local. Nous pouvons obtenir la même chose dans un composant fonctionnel en utilisant le useEffect accrocher.

import { useState, useEffect } from "react";

const StateChangesHooks = () => {
  const [counter, setCounter] = useState(0);

  const increment = () => setCounter(counter => counter + 1);
  const decrement = () => setCounter(counter => counter - 1);

  useEffect(() => {
    console.log("Current counter", counter);
    localStorage.setItem("counter", counter);
  }, [counter]);

  return (
    <div>
      <h2>Reacting To State Changes - Hooks</h2>
      <div>Count: {counter}</div>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
      </div>
    </div>
  );
};

export default StateChangesHooks;

La useEffect hook attend deux arguments : une fonction de rappel et un tableau de dépendances. Ce crochet s’exécute toujours au moins une fois après le montage du composant. Ensuite, il ne s’exécute que lorsque l’une des valeurs transmises à l’intérieur du tableau de dépendances change. Si le tableau de dépendances est passé au useEffect est vide, l’effet ne s’exécute qu’une seule fois. Dans notre exemple, chaque fois que le counter changements d’état, les useEffect exécute la fonction qui enregistre le counter dans le stockage local.

Récupération des données

Si vous souhaitez récupérer des données dans des composants de classe, vous devez initialiser une requête API dans le componentDidMount cycle de la vie. Dans l’exemple de code ci-dessous, nous récupérons et affichons une liste de messages.

import { Component } from "react";

class FetchingDataClass extends Component {
  state = {
    posts: [],
  };

  componentDidMount() {
    this.fetchPosts();
  }

  fetchPosts = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const data = await response.json();
    this.setState({
      posts: data.slice(0, 10),
    });
  };

  render() {
    return (
      <div>
        <h2>Fetching Data - Class</h2>
        <div>
          {this.state.posts.map(post => {
            return <div key={post.id}>{post.title}</div>;
          })}
        </div>
      </div>
    );
  }
}

export default FetchingDataClass;

Avec les crochets, nous pouvons à nouveau utiliser le useEffect accrocher. Comme je l’ai mentionné précédemment, le useEffect hook s’exécute une fois après le premier montage du composant, puis chaque fois que les dépendances transmises changent. Nous nous assurons que le useEffect ne s’exécute qu’une seule fois en passant un tableau vide comme deuxième argument pour l’argument des dépendances.

import { useState, useEffect } from "react";

const FetchingDataHooks = () => {
  const [posts, setPosts] = useState([]);

  const fetchPosts = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const data = await response.json();
    setPosts(data.slice(0, 10));
  };

  useEffect(() => {
    fetchPosts();
  }, []);

  return (
    <div>
      <h2>Fetching Data - Hooks</h2>
      <div>
        {posts.map(post => {
          return <div key={post.id}>{post.title}</div>;
        })}
      </div>
    </div>
  );
};

export default FetchingDataHooks;

Nettoyage lorsque le composant est démonté

Nettoyer lorsqu’un composant est démonté est assez important, sinon nous pourrions nous retrouver avec des fuites de mémoire. Par exemple, dans un composant, nous pourrions vouloir écouter un événement comme resize ou scroll et faites quelque chose en fonction de la taille de la fenêtre ou de la position du défilement. Ci-dessous, vous pouvez voir un exemple de composant de classe qui écoute le resize événement, puis met à jour l’état avec la largeur et la hauteur de la fenêtre. L’écouteur d’événement est supprimé dans le componentWillUnmount cycle de la vie.

import { Component } from "react";

class CleanupClass extends Component {
  state = {
    width: window.innerWidth,
    height: window.innerHeight,
  };

  componentDidMount() {
    window.addEventListener("resize", this.updateWindowSize, {
      passive: true,
    });
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.updateWindowSize, {
      passive: true,
    });
  }

  updateWindowSize = () => {
    this.setState({
      width: window.innerWidth,
      height: window.innerHeight,
    });
  };

  render() {
    return (
      <div>
        <h2>Cleanup - Class</h2>
        <div>
          Window Size: {this.state.width} x {this.state.height}
        </div>
      </div>
    );
  }
}

export default CleanupClass;

Il y a une caractéristique du useEffect crochet que nous n’avons pas encore couvert. Nous pouvons effectuer un nettoyage dans un composant en renvoyant une fonction à partir du rappel qui a été passé au useEffect. Cette fonction est appelée lorsque le composant est démonté. Comme le montre l’exemple ci-dessous, nous définissons d’abord updateWindowSize fonction, puis ajoutez la resize écouteur d’événement à l’intérieur de useEffect. Ensuite, nous renvoyons une fonction de flèche anonyme qui supprimera l’écouteur.

import { useState, useEffect } from "react";

const CleanupHooks = () => {
  const [width, setWidth] = useState(window.innerWidth);
  const [height, setHeight] = useState(window.innerHeight);

  useEffect(() => {
    const updateWindowSize = () => {
      setWidth(window.innerWidth);
      setHeight(window.innerHeight);
    };

    window.addEventListener("resize", updateWindowSize, {
      passive: true,
    });

    return () => {
      window.removeEventListener("resize", this.updateWindowSize, {
        passive: true,
      });
    };
  }, []);

  return (
    <div>
      <h2>Cleanup - Hooks</h2>
      <div>
        Window Size: {width} x {height}
      </div>
    </div>
  );
};

export default CleanupHooks;

Empêcher le composant de re-rendre

React est très rapide et nous n’avons généralement pas à nous soucier d’une optimisation prématurée. Cependant, il existe des cas dans lesquels il est utile d’optimiser les composants et de s’assurer qu’ils ne sont pas rendus trop souvent.

Par exemple, une manière courante d’optimiser les composants de classe consiste soit à utiliser un PureComponent ou la shouldComponentUpdate crochet de cycle de vie. L’exemple ci-dessous montre deux composants de classe : un parent et un enfant. Le parent a deux valeurs avec état :counter et fruit. Le composant enfant ne doit être restitué que lorsque le fruit la valeur change, nous utilisons donc le shouldComponentUpdate cycle de vie pour vérifier si le fruit accessoire changé. Si c’est la même chose, le composant enfant ne sera pas restitué.

Parent de classe qui provoque un nouveau rendu

import { Component } from "react";
import PreventRerenderClass from "./PreventRerenderClass.jsx";

function randomInteger(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

const fruits = ["banana", "orange", "apple", "kiwi", "mango"];

class PreventRerenderExample extends Component {
  state = {
    fruit: null,
    counter: 0,
  };

  pickFruit = () => {
    const fruitIdx = randomInteger(0, fruits.length - 1);
    const nextFruit = fruits[fruitIdx];

    this.setState({
      fruit: nextFruit,
    });
  };

  componentDidMount() {
    this.pickFruit();
  }

  render() {
    return (
      <div>
        <h2>Prevent Rerender Class Example</h2>
        <h3>
          Current fruit: {this.state.fruit} | counter: {this.state.counter}
        </h3>

        <button onClick={this.pickFruit}>Pick a fruit</button>
        <button
          onClick={() =>
            this.setState(({ counter }) => ({
              counter: counter + 1,
            }))
          }
        >
          Increment
        </button>
        <button
          onClick={() =>
            this.setState(({ counter }) => ({ counter: counter - 1 }))
          }
        >
          Decrement
        </button>
        <div className="section">
          <PreventRerenderClass fruit={this.state.fruit} />
        </div>
      </div>
    );
  }
}

export default PreventRerenderExample;

Enfant de classe avec shouldComponentUpdate

import { Component } from "react";

class PreventRerenderClass extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    return this.props.fruit !== nextProps.fruit;
  }

  render() {
    console.log("PreventRerenderClass rendered");
    return (
      <div>
        <p>Fruit: {this.props.fruit}</p>
      </div>
    );
  }
}

export default PreventRerenderClass;

Avec l’introduction des crochets, nous avons obtenu un nouveau composant d’ordre supérieur appelé memo. Il peut être utilisé pour optimiser les performances et empêcher le rendu des composants fonctionnels. Ci-dessous, nous avons une implémentation avec des crochets.

Hooks parent qui provoque un nouveau rendu

import { useState, useEffect } from "react";
import PreventRerenderHooks from "./PreventRerenderHooks.jsx";

function randomInteger(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

const fruits = ["banana", "orange", "apple", "kiwi", "mango"];

const PreventRerenderExample = () => {
  const [fruit, setFruit] = useState(null);
  const [counter, setCounter] = useState(0);

  const pickFruit = () => {
    const fruitIdx = randomInteger(0, fruits.length - 1);
    const nextFruit = fruits[fruitIdx];
    setFruit(nextFruit);
  };

  useEffect(() => {
    pickFruit();
  }, []);

  return (
    <div>
      <h2>Prevent Rerender Hooks Example</h2>
      <h3>
        Current fruit: {fruit} | counter: {counter}
      </h3>

      <button onClick={pickFruit}>Pick a fruit</button>
      <button onClick={() => setCounter(counter => counter + 1)}>
        Increment
      </button>
      <button onClick={() => setCounter(counter => counter - 1)}>
        Decrement
      </button>
      <div className="section">
        <PreventRerenderHooks fruit={fruit} />
      </div>
    </div>
  );
};

export default PreventRerenderExample;

Crochets enfant avec memo

import { memo } from "react";

const PreventRerenderHooks = props => {
  console.log("PreventRerenderHooks rendered");
  return (
    <div>
      <p>Fruit: {props.fruit}</p>
    </div>
  );
};

export default memo(PreventRerenderHooks);

La PreventRerenderHooks composant est enveloppé avec le memo composant et n’effectue un nouveau rendu que si le fruit changements d’accessoires. Notez que le memo Le composant effectue une comparaison superficielle sous le capot, donc si vous avez besoin de plus de contrôle sur le moment où le composant enveloppé doit être restitué, vous pouvez fournir votre propre fonction pour effectuer la comparaison des accessoires.

import { memo } from "react";

const PreventRerenderHooks = props => {
  console.log("PreventRerenderHooks rendered");
  return (
    <div>
      <p>Fruit: {props.fruit}</p>
    </div>
  );
};

export default memo(PreventRerenderHooks, (prevProps, nextProps) => {
  return prevProps.fruit !== nextProps.fruit
});

API de contexte

L’API de contexte est un excellent outil pour fournir des valeurs aux composants à différents niveaux de la hiérarchie des composants. Un nouveau contexte peut être créé en utilisant le createContext méthode proposée par React. Pour cet exemple, nous aurons deux contextes, l’un pour l’état de l’utilisateur et l’autre pour la méthode de mise à jour.

userContextuserContext

import { createContext } from "react";

export const UserContext = createContext();
export const UserActionsContext = createContext();

Commençons par l’exemple de composant de classe. Dans le composant parent, nous fournissons le user état et setUser méthode aux consommateurs.

Fournisseur de contexte de classe

import { Component, createContext } from "react";
import ContextApiClassConsumer from "./ContextApiClassConsumer.jsx";
import { UserContext, UserActionsContext } from "./userContext.js";

class ContextApiHooksProvider extends Component {
  state = {
    user: {
      name: "Thomas Class",
    },
  };

  setUser = user => this.setState({ user });

  render() {
    console.log("in render class user", this.state.user);
    return (
      <UserContext.Provider value={this.state.user}>
        <UserActionsContext.Provider value={this.setUser}>
          <ContextApiClassConsumer />
        </UserActionsContext.Provider>
      </UserContext.Provider>
    );
  }
}

export default ContextApiHooksProvider;

Nous pouvons consommer le contexte dans un composant de classe en utilisant le Context.Consumer composant disponible dans tous les contextes. Ce composant accepte une fonction en tant qu’enfant qui reçoit une valeur de contexte en tant qu’argument.

Consommateur de contexte de classe

import { Component } from "react";
import { UserContext, UserActionsContext } from "./userContext.js";

class ContextApiClassConsumer extends Component {
  render() {
    return (
      <UserContext.Consumer>
        {user => (
          <UserActionsContext.Consumer>
            {setUser => (
              <div>
                <h2>ContextApiClass Consumer</h2>
                <input
                  type="text"
                  value={user.name}
                  onChange={e =>
                    setUser({
                      name: e.target.value,
                    })
                  }
                />
              </div>
            )}
          </UserActionsContext.Consumer>
        )}
      </UserContext.Consumer>
    );
  }
}

export default ContextApiClassConsumer;

Comme le montre l’exemple ci-dessus, la fonction enfant du UserContext.Consumer composant reçoit le user état, et la fonction enfant du UserActionsContext.Consumer reçoit le setUser méthode.

L’exemple du fournisseur de crochets est très similaire mais beaucoup plus concis. Encore une fois, nous utilisons le UserContext.Provider et UserActionsContext.Provider composant pour fournir le user l’état et le setUser méthode.

Fournisseur de contexte de crochets

import { useState } from "react";
import ContextApiHooksConsumer from "./ContextApiHooksConsumer.jsx";
import { UserContext, UserActionsContext } from "./userContext.js";

const ContextApiHooksProvider = () => {
  const [user, setUser] = useState({
    name: "Thomas Hooks",
  });
  return (
    <UserContext.Provider value={user}>
      <UserActionsContext.Provider value={setUser}>
        <ContextApiHooksConsumer />
      </UserActionsContext.Provider>
    </UserContext.Provider>
  );
};

export default ContextApiHooksProvider;

Techniquement, dans un composant fonctionnel, nous pourrions consommer le contexte de la même manière que nous l’avons fait dans le composant de classe. Cependant, il existe une approche beaucoup plus propre avec les crochets, car nous pouvons utiliser le useContext crochet pour accéder aux valeurs de contexte.

Hooks Contexte Consommateur

import { useContext } from "react";
import { UserContext, UserActionsContext } from "./userContext.js";

const ContextApiHooksConsumer = () => {
  const user = useContext(UserContext);
  const setUser = useContext(UserActionsContext);
  return (
    <div>
      <h2>ContextApiHooks Consumer</h2>
      <input
        type="text"
        value={user.name}
        onChange={e =>
          setUser({
            name: e.target.value,
          })
        }
      />
    </div>
  );
};

export default ContextApiHooksConsumer;

Si vous souhaitez en savoir plus sur la façon d’utiliser l’API Context de manière performante, j’ai juste le article pour toi.

Préserver les valeurs à travers les re-rendus

Il existe des scénarios dans lesquels nous pourrions avoir besoin de stocker certaines données dans un composant, mais nous ne voudrions pas nécessairement les stocker dans l’état, car l’interface utilisateur ne s’appuie en aucune façon sur ces données.

Par exemple, nous pouvons enregistrer certaines métadonnées que nous aimerions inclure plus tard dans une requête API. Ceci est très facile à réaliser dans un composant de classe, car nous pouvons simplement attribuer une nouvelle propriété à la classe.

import { Component } from "react";

class PreservingValuesClass extends Component {
  state = {
    counter: 0,
  };

  componentDidMount() {
    this.valueToPreserve = Math.random();
  }

  showValue = () => {
    alert(this.valueToPreserve);
  };

  increment = () => this.setState(({ counter }) => ({ counter: counter + 1 }));

  render() {
    console.log("PreventRerenderClass rendered");
    return (
      <div>
        <h2>Preserving Values - Class</h2>
        <p>Counter: {this.state.counter}</p>
        <button onClick={this.increment}>Increment</button>
        <button onClick={this.showValue}>Show value</button>
      </div>
    );
  }
}

export default PreservingValuesClass;

Dans cet exemple, lorsque le composant est monté, nous attribuons un nombre aléatoire dynamique sur le valueToPreserve propriété. Nous avons également l’incrément du compteur pour forcer un nouveau rendu et le Show value pour afficher la valeur conservée dans une alerte.

Comme je l’ai dit, avec un composant de classe, c’est facile, mais ce n’est pas si simple dans un composant fonctionnel. La raison en est qu’à chaque fois qu’un composant fonctionnel est restitué, tout ce qu’il contient doit être réexécuté. Cela signifie que si nous avons un composant comme celui-ci :

const MyComponent = props => {
  const valueToPreserve = Math.random()
 	
}

La Math.random() sera appelée à chaque nouveau rendu, de sorte que la première valeur créée sera perdue.

Une façon d’éviter ce problème serait de déplacer la variable en dehors du composant. Cela ne fonctionnerait pas, cependant, car si le composant était utilisé plusieurs fois, la valeur serait remplacée par chacun d’eux.

Heureusement, React fournit un crochet qui convient parfaitement à ce cas d’utilisation. Nous pouvons préserver les valeurs à travers les re-rendus dans les composants fonctionnels en utilisant le useRef accrocher.

import { useState, useRef, useEffect } from "react";

const PreserveValuesHooks = props => {
  const valueToPreserve = useRef(null);
  const [counter, setCounter] = useState(0);

  const increment = () => setCounter(counter => counter + 1);

  const showValue = () => {
    alert(valueToPreserve.current);
  };

  useEffect(() => {
    valueToPreserve.current = Math.random();
  }, []);

  return (
    <div>
      <h2>Preserving Values - Class</h2>
      <p>Counter: {counter}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={showValue}>Show value</button>
    </div>
  );
};

export default PreserveValuesHooks;

La valueToPreserve est une référence qui commence initialement par le null évaluer. Cependant, il est modifié plus tard dans le useEffect à un nombre aléatoire que nous voulons conserver.

Comment exposer l’état et les méthodes à un composant parent

Bien que nous ne devrions pas avoir à accéder souvent à l’état et aux propriétés d’un composant enfant, il existe des situations dans lesquelles cela peut être utile, par exemple, si nous voulons réinitialiser une partie de l’état du composant ou accéder à son état. Nous devons créer une référence dans laquelle nous pouvons stocker une référence au composant enfant auquel nous voulons accéder. Dans un composant de classe, nous pouvons utiliser le createRef méthode, puis passez cette référence au composant enfant.

Exposer le parent de la classe des propriétés

import { Component, createRef } from "react";
import ExposePropertiesClassChild from "./ExposePropertiessClassChild";

class ExposePropertiesClassParent extends Component {
  constructor(props) {
    super(props);
    this.childRef = createRef();
  }

  showValues = () => {
    const counter = this.childRef.current.state.counter;
    const multipliedCounter = this.childRef.current.getMultipliedCounter();
    alert(`
      counter: ${counter}
      multipliedCounter: ${multipliedCounter}
    `);
  };

  increment = () => this.setState(({ counter }) => ({ counter: counter + 1 }));

  render() {
    return (
      <div>
        <h2>Expose Properties - Class</h2>
        <button onClick={this.showValues}>Show child values</button>
        <ExposePropertiesClassChild ref={this.childRef} />
      </div>
    );
  }
}

export default ExposePropertiesClassParent;

La showValues méthode récupère le counter état et utilise le getMultipliedCounter méthode. Ci-dessous, vous pouvez voir le composant enfant de la classe.

Exposer les propriétés de la classe enfant

import { Component } from "react";

class ExposePropertiesClassChild extends Component {
  state = {
    counter: 0,
  };

  getMultipliedCounter = () => {
    return this.state.counter * 2;
  };

  increment = () => this.setState(({ counter }) => ({ counter: counter + 1 }));

  render() {
    return (
      <div>
        <p>Counter: {this.state.counter}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

export default ExposePropertiesClassChild;

Pour accéder aux propriétés du composant enfant, nous n’avions qu’à créer une référence dans le composant parent et à la transmettre. Voyons maintenant comment nous pouvons obtenir la même chose avec des composants fonctionnels et des crochets.

Exposer les propriétés des crochets parent

import { useRef } from "react";
import ExposePropertiesHooksChild from "./ExposePropertiesHooksChild";

const ExposePropertiesHooksParent = props => {
  const childRef = useRef(null);

  const showValues = () => {
    const counter = childRef.current.counter;
    const multipliedCounter = childRef.current.getMultipliedCounter();
    alert(`
      counter: ${counter}
      multipliedCounter: ${multipliedCounter}
    `);
  };

  return (
    <div>
      <h2>Expose Properties - Hooks</h2>
      <button onClick={showValues}>Show child values</button>
      <ExposePropertiesHooksChild ref={childRef} />
    </div>
  );
};

export default ExposePropertiesHooksParent;

Dans le parent, nous utilisons le useRef crochet pour stocker une référence au composant enfant. La valeur de la childRef est ensuite accessible dans le showValues fonction. Comme vous pouvez le voir, l’implémentation est assez similaire à celle du composant de classe.

Cependant, nous n’avons pas encore terminé, car nous devons exposer manuellement les propriétés du composant fonctionnel. Nous pouvons le faire en utilisant le forwardRef et useImperativeHandle accrocher.

Exposez les propriétés Hooks Child

import { useState, useImperativeHandle, forwardRef } from "react";

const ExposePropertiesHooksChild = (props, ref) => {
  const [counter, setCounter] = useState(0);

  const increment = () => setCounter(counter => counter + 1);

  useImperativeHandle(ref, () => {
    return {
      counter,
      getMultipliedCounter: () => counter * 2,
    };
  });

  return (
    <div>
      <p>Counter: {counter}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

export default forwardRef(ExposePropertiesHooksChild);

La forwardRef transmet essentiellement la référence transmise du parent au composant, tandis que useImperativeHandle spécifie ce qui doit être accessible au composant parent.

Sommaire

J’espère que vous avez maintenant une meilleure idée de la façon dont vous pouvez convertir vos composants de classe en crochets. Avant de commencer à convertir tous vos composants, assurez-vous de passer par le documentation officielle des crochetscar certaines règles doivent être suivies, telles que le fait que les crochets ne peuvent pas être appelés de manière conditionnelle.

Après avoir travaillé longtemps avec des crochets, je peux seulement dire que cela vaut vraiment la peine de les maîtriser. Ils offrent de nombreux avantages, tels qu’un code plus concis et une meilleure réutilisation de la logique avec état.




Source link