Composants fonctionnels avec React Hooks. Pourquoi c'est mieux?

Lorsque React.js 16.8 a été publié, nous avons eu l'occasion d'utiliser React Hooks. Les crochets nous permettent d'écrire des composants entièrement fonctionnels à l'aide de fonctions. Nous pouvons utiliser toutes les fonctionnalités de React.js et le faire de manière plus pratique.


Beaucoup de gens ne sont pas d'accord avec la conception de Hooks. Dans cet article, je voudrais parler de certains avantages importants que React Hooks vous offre et pourquoi nous devons écrire avec Hooks.


Je ne parlerai pas de l'utilisation des crochets. Ce n'est pas très important pour les exemples. Si vous souhaitez lire quelque chose sur ce sujet, vous pouvez utiliser la documentation officielle . De plus, si ce sujet vous intéresse, je vous en dirai plus sur Hooks.


Les crochets nous permettent de réutiliser facilement notre code


Imaginons un composant rendant une forme simple. Cela peut être quelque chose qui nous montre quelques entrées et nous permet de changer ses valeurs.


Avec la notation de classe, il y aura quelque chose comme ceci:


class Form extends React.Component { state = { // Fields values fields: {}, }; render() { return ( <form> {/* Inputs render */} </form> ); }; } 

Imaginons maintenant que nous voulons enregistrer automatiquement nos valeurs de champs dans un backend chaque fois qu'elles changent. Je suggère de sauter la définition des fonctions externes comme shallowEqual et shallowEqual .


 class Form extends React.Component { constructor(props) { super(props); this.saveToDraft = debounce(500, this.saveToDraft); }; state = { // Fields values fields: {}, // Draft saving meta draft: { isSaving: false, lastSaved: null, }, }; saveToDraft = (data) => { if (this.state.isSaving) { return; } this.setState({ isSaving: true, }); makeSomeAPICall().then(() => { this.setState({ isSaving: false, lastSaved: new Date(), }) }); } componentDidUpdate(prevProps, prevState) { if (!shallowEqual(prevState.fields, this.state.fields)) { this.saveToDraft(this.state.fields); } } render() { return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); }; } 

Le même composant avec des crochets:


 const Form = () => { // Our state const [fields, setFields] = useState({}); const [draftIsSaving, setDraftIsSaving] = useState(false); const [draftLastSaved, setDraftLastSaved] = useState(false); useEffect(() => { const id = setTimeout(() => { if (draftIsSaving) { return; } setDraftIsSaving(true); makeSomeAPICall().then(() => { setDraftIsSaving(false); setDraftLastSaved(new Date()); }); }, 500); return () => clearTimeout(id); }, [fields]); return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); } 

Comme nous le voyons, il n'y a pas de grande différence ici. Nous avons remplacé this.state par le crochet useState et enregistré le brouillon dans le crochet useEffect maintenant.


La différence que je veux montrer ici est (il y a aussi d'autres différences, mais je vais me concentrer sur celle-ci): nous pouvons facilement extraire ce code de notre composant et l'utiliser ailleurs:


 // useDraft hook can be used in any other component const useDraft = (fields) => { const [draftIsSaving, setDraftIsSaving] = useState(false); const [draftLastSaved, setDraftLastSaved] = useState(false); useEffect(() => { const id = setTimeout(() => { if (draftIsSaving) { return; } setDraftIsSaving(true); makeSomeAPICall().then(() => { setDraftIsSaving(false); setDraftLastSaved(new Date()); }); }, 500); return () => clearTimeout(id); }, [fields]); return [draftIsSaving, draftLastSaved]; } const Form = () => { // Our state const [fields, setFields] = useState({}); const [draftIsSaving, draftLastSaved] = useDraft(fields); return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); } 

Et nous pouvons utiliser le crochet useDraft dans d'autres composants! C'est, bien sûr, un exemple très simple, mais la réutilisation de code est assez importante pour cela et l'exemple montre à quel point c'est facile avec Hooks.


Les crochets nous permettent d'écrire des composants de manière plus intuitive


Imaginons un rendu de composant de classe, par exemple, un écran de discussion, une liste de discussions et un formulaire de message. Comme ça:


 class ChatApp extends React.Component { state = { currentChat: null, }; handleSubmit = (messageData) => { makeSomeAPICall(SEND_URL, messageData) .then(() => { alert(`Message is sent to chat ${this.state.currentChat}`); }); }; render() { return ( <Fragment> <ChatsList changeChat={currentChat => { this.setState({ currentChat }); }} /> <CurrentChat id={currentChat} /> <MessageForm onSubmit={this.handleSubmit} /> </Fragment> ); }; } 

Imaginez ensuite notre utilisateur utilisant ce composant de chat:


  • Ils ouvrent le chat 1
  • Ils envoient un message (imaginons un réseau lent)
  • Ils ouvrent le chat 2
  • Ils voient une alerte concernant leur message:
    • "Le message est envoyé au chat 2"

Mais ils ont envoyé un message au deuxième chat, comment cela s'est-il passé? C'est parce que la méthode de classe fonctionne avec la valeur actuelle, pas la valeur que nous avions lorsque nous commencions une demande de message. Ce n'est pas grave avec des composants simples comme celui-ci, mais cela peut être une source de bugs dans des systèmes plus complexes.


En revanche, les composants fonctionnels agissent d'une autre manière:


 const ChatApp = () => { const [currentChat, setCurrentChat] = useState(null); const handleSubmit = useCallback( (messageData) => { makeSomeAPICall(SEND_URL, messageData) .then(() => { alert(`Message is sent to chat ${currentChat}`); }); }, [currentChat] ); render() { return ( <Fragment> <ChatsList changeChat={setCurrentChat} /> <CurrentChat id={currentChat} /> <MessageForm onSubmit={handleSubmit} /> </Fragment> ); }; } 

Imaginons notre utilisateur:


  • Ils ouvrent le chat 1
  • Ils envoient un message (imaginons un réseau lent)
  • Ils ouvrent le chat 2
  • Ils voient une alerte concernant leur message:
    • "Le message est envoyé au chat 1"

Eh bien, qu'est-ce qui a changé? Maintenant, nous travaillons avec une valeur, capturée au moment du rendu. Nous créons un nouveau handleSubmit chaque fois que currentChat change. Cela nous permet d'oublier les changements futurs et d'y penser maintenant .


Chaque composant rend la capture de tout ce qu'il utilise .


Les crochets font disparaître le cycle de vie des composants


Cette raison recoupe fortement la précédente. React est une bibliothèque d'interface utilisateur déclarative. La déclarativité rend la création et le traitement de l'interface utilisateur beaucoup plus faciles. Cela nous permet d'oublier les changements impératifs du DOM.


Même ainsi, lorsque nous utilisons des classes, nous sommes confrontés au cycle de vie des composants. Cela ressemble à ceci:


  • Montage
  • Mise à jour (chaque fois que l' state ou les props changent)
  • Démontage

Cela semble pratique mais je l'ai convaincu uniquement à cause de nos habitudes. Ce n'est pas comme React.


Au lieu de cela, les composants fonctionnels nous permettent d'écrire le code des composants et d'oublier le cycle de vie. Nous ne pensons qu'à la synchronisation . Nous écrivons la fonction rend notre interface utilisateur à partir des accessoires d'entrée et de l'état intérieur.


Au début, le crochet useEffect semble remplacer le componentDidMount , le componentDidUpdate et d'autres méthodes de cycle de vie. Mais ce n'est pas comme ça. Lorsque nous utilisons useEffect nous avons dit à React: "Hé, faites ceci après avoir rendu mon composant".


Voici un bon exemple du grand article sur useEffect :


  • Réagissez: donnez-moi l'interface utilisateur lorsque l'état est 0 .
  • Votre composant:
    • Voici le résultat du rendu: <p>You clicked 0 times</p> .
    • N'oubliez pas non plus d'exécuter cet effet une fois que vous avez terminé: () => { document.title = 'You clicked 0 times' } .
  • Réagissez: Bien sûr. Mise à jour de l'interface utilisateur. Hé navigateur, j'ajoute des trucs au DOM.
  • Navigateur: Cool, je l'ai peint à l'écran.
  • Réagissez: OK, je vais maintenant exécuter l'effet que vous m'avez donné.
    • Running () => { document.title = 'You clicked 0 times' } .

C'est beaucoup plus déclaratif, n'est-ce pas?


En conclusion


React Hooks nous permet de nous débarrasser de quelques problèmes et de faciliter le développement. Nous avons juste besoin de changer notre modèle mental. Le composant fonctionnel est en fait une fonction de l'interface utilisateur des accessoires. Ils décrivent comment tout cela doit être à tout moment et nous aident à oublier les changements.


Eh bien, nous devons apprendre à l'utiliser, mais bon, avez-vous écrit correctement les composants d'une classe la première fois?

Source: https://habr.com/ru/post/fr443500/


All Articles