Cet article explore les composants apatrides dans React. Ce type de composant ne contient pas d'appels this.state = { ... }
et ne traite que des composants «accessoires» entrants et enfants.
Résumé des points clés
this.state = { … }
. Ils ne gèrent que les «accessoires» et sous-composants entrants, ce qui les rend plus simples et plus faciles à analyser du point de vue du test. shouldComponentUpdate
intégrée qui permet des comparaisons peu profondes de chaque accessoire. bases
import React, { Component } from 'react' class User extends Component { render() { const { name, highlighted, userSelected } = this.props console.log('Hey User is being rendered for', [name, highlighted]) return <div> <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}} onClick={event => { userSelected() }}> {name} </h3> </div> } }
Le code s'exécute normalement. C'est très basique, mais il construit des exemples.
Il faut noter:
this.state = { ... }
. console.log
est utilisé pour comprendre son utilisation. Surtout lorsque vous effectuez une optimisation des performances, si les accessoires ne sont pas réellement modifiés, le réinstallation inutile doit être évité. .bind(this)
. Afficher le composant
Nous réalisons maintenant que le composant ci-dessus n'est pas seulement apatride, c'est en fait ce que Dan Abramov appelle un composant d'affichage. C'est juste un nom, mais fondamentalement, il est léger, produit du HTML / DOM et ne gère aucune données d'état.
afin que nous puissions en faire une fonction! Non seulement cela semble "cool", mais il le rend également moins terrible car il est plus facile à comprendre. Il reçoit des entrées et, indépendamment de l'environnement, renvoie toujours la même sortie. Bien sûr, il "rappelle" parce que l'un des accessoires est une fonction appelable.
Réécrivons-le:
import React, { Component } from 'react' class User extends Component { render() { const { name, highlighted, userSelected } = this.props console.log('Hey User is being rendered for', [name, highlighted]) return <div> <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}} onClick={event => { userSelected() }}> {name} </h3> </div> } }
ça fait du bien, non? Cela ressemble à de pur javascript, vous pouvez l'écrire sans penser au cadre que vous utilisez.
Le problème de la rediffusion continue
Supposons que notre composant utilisateur est utilisé dans un composant qui change l'état au fil du temps. Mais l'État n'affecte pas nos composants. Par exemple, comme ceci:
const User = ({ name, highlighted, userSelected }) => { console.log('Hey User is being rendered for', [name, highlighted]) return <div> <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}} onClick={event => { userSelected() }}> {name} </h3> </div> }
Exécutez ce code et vous remarquerez que même si rien ne change, nos composants seront renvoyés! Ce n'est pas un gros problème maintenant, mais dans les applications pratiques, les composants ont tendance à devenir de plus en plus complexes, et chaque rediffusion inutile entraînera le ralentissement du site Web.
Si vous utilisez react-addons-perf
pour déboguer cette application maintenant, je suis sûr que vous constaterez que le temps est perdu sur le rendu Users->User
. Oups! ce qu'il faut faire? !
Il semble que tout montre que nous devons utiliser shouldComponentUpdate
pour couvrir la façon dont React pense que les accessoires sont différents, et nous sommes sûrs qu'ils ne sont pas différents. Afin d'ajouter un crochet de cycle de vie React, le composant doit être une classe. Hélas . Nous retournons donc à l'implémentation basée sur les classes originales et ajoutons une nouvelle méthode de crochet de lifting:
Retour vers le composant de classe
import React, { Component } from 'react' class Users extends Component { constructor(props) { super(props) this.state = { otherData: null, users: [{name: 'John Doe', highlighted: false}] } } async componentDidMount() { try { let response = await fetch('https://api.github.com') let data = await response.json() this.setState({otherData: data}) } catch(err) { throw err } } toggleUserHighlight(user) { this.setState(prevState => ({ users: prevState.users.map(u => { if (u.name === user.name) { u.highlighted = !u.highlighted } return u }) })) } render() { return <div> <h1>Users</h1> { this.state.users.map(user => { return <User name={user.name} highlighted={user.highlighted} userSelected={() => { this.toggleUserHighlight(user) }}/> }) } </div> } }
Faites attention à la méthode nouvellement ajoutée shouldComponentUpdate
. C'est un peu moche. Non seulement nous ne pouvons plus utiliser de fonctions, mais nous devons également énumérer manuellement les accessoires qui peuvent être modifiés. Cela implique une hypothèse audacieuse que l'hélice de fonction userSelected
ne changera pas. Cela est peu probable, mais cela a besoin d'attention.
mais veuillez noter que même si le composant d'application inclus est renvoyé, cela ne sera rendu qu'une seule fois! Donc, c'est bon pour les performances. Mais pouvons-nous faire mieux?
react.pureComponent
Depuis React 15.3, il existe une nouvelle classe de base de composants. Il s'appelle PureComponent
, et il a une méthode shouldComponentUpdate
intégrée qui fait une "comparaison peu profonde" de chaque accessoire. merveilleux! Si nous l'utilisons, nous pouvons supprimer notre méthode personnalisée shouldComponentUpdate
qui doit répertorier des accessoires spécifiques.
import React, { Component } from 'react' class User extends Component { render() { const { name, highlighted, userSelected } = this.props console.log('Hey User is being rendered for', [name, highlighted]) return <div> <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}} onClick={event => { userSelected() }}> {name} </h3> </div> } }
Essayez-le et vous serez déçu. Il se redevient à chaque fois. Pourquoi? ! La réponse est due au fait que la fonction userSelected
est recréée à chaque fois dans la méthode render
de l'application. Cela signifie que lorsque le composant basé sur un PureComponent
appelle son propre shouldComponentUpdate()
, il renvoie true
, car la fonction est toujours différente, car elle est créée à chaque fois.
Habituellement, la solution à ce problème est de lier la fonction du constructeur qui contient le composant. Premièrement, si nous faisons cela, cela signifie que nous devons taper le nom de la méthode 5 fois (et 1 avant):
this.userSelected = this.userSelected.bind(this)
(dans le constructeur) userSelected() { ... }
(en tant que définition de la méthode elle-même) <User ... userSelected={this.userSelected} ... />
(où le rendu du composant utilisateur est défini) Un autre problème est que, comme vous pouvez le voir, lorsque la méthode userSelected
est réellement exécutée, cela dépend d'une fermeture. En particulier, il s'appuie sur la variable de portée this.state.users.map()
de l'itérateur. user
à userSelected
puis lorsque la méthode est appelée (dans un composant enfant), passez le this
(ou son nom ) revenez en arrière. Voici une solution comme celle-ci. user
Recomposer pour sauver!
Tout d'abord, passons en revue nos objectifs:Selon la documentation, Recompose est "une bibliothèque d'outils utilitaires React pour les composants de la fonction et les composants avancés. Considérez-le comme un Lodash pour React."
. Il y a beaucoup à explorer dans cette bibliothèque, mais maintenant nous voulons rendre nos composants de fonction sans rediffusion lorsque les accessoires n'ont pas changé. La première fois que nous avons essayé de le réécrire dans le composant de fonction avec Recompose.pure, il ressemble à ceci:
import React, { Component } from 'react' class User extends Component { render() { const { name, highlighted, userSelected } = this.props console.log('Hey User is being rendered for', [name, highlighted]) return <div> <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}} onClick={event => { userSelected() }}> {name} </h3> </div> } }
Vous remarquerez peut-être que si vous exécutez ce code, le composant utilisateur se renvoie toujours même si les clés des accessoires (
et) n'ont pas changé. name
highlighted
allons plus loin. Nous n'utilisons pas
, qui est une version de recompose.pure
, mais vous pouvez spécifier la clé de l'hélice sur laquelle vous souhaitez vous concentrer: recompose.onlyUpdateForKeys
recompose.pure
const User = ({ name, highlighted, userSelected }) => { console.log('Hey User is being rendered for', [name, highlighted]) return <div> <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}} onClick={event => { userSelected() }}> {name} </h3> </div> }
Après avoir exécuté ce code, vous remarquerez qu'il ne sera mis à jour que lorsque
ou les accessoires changent. Si le composant parent est renvoyé, le composant utilisateur ne le fait pas. name
highlighted
Vive! Nous avons trouvé de l'or!
Discussion
Tout d'abord, demandez-vous si cela vaut la peine d'optimiser les performances de votre composant. Peut-être que c'est plus que ça en vaut la peine. Vos composants doivent être légers, vous pouvez peut-être déplacer tous les calculs coûteux des composants et les déplacer dans des fonctions mémorables externes, ou vous pouvez réorganiser vos composants afin que certaines données ne puissent pas être un gaspillage de composants de rendu lorsqu'ils sont utilisés. Par exemple, dans cet exemple, vous ne voudrez peut-être pas rendre le composant utilisateur avant que Fetch ne soit terminé. Écrire le code de la manière la plus pratique pour vous, puis démarrer votre programme et itérer à partir de là pour le rendre meilleur n'est pas une mauvaise solution. Dans cet exemple, afin d'améliorer les performances, vous devez définir le composant de fonction à partir de: … changé en… Idéalement, plutôt que de montrer un moyen de contourner le problème, la meilleure solution est un nouveau patch de React, ce qui fait une énorme amélioration à d'accord! Il existe une alternative de compromis à devoir faire face aux fonctions en ligne qui lient les méthodes dans le constructeur et chaque récréation. C'est le domaine de la classe publique. Il s'agit d'une fonctionnalité de phase 2 dans Babel, donc votre configuration le soutiendra probablement. Par exemple, voici une branche qui l'utilise, ce qui est non seulement plus court, mais maintenant, nous n'avons pas besoin de répertorier manuellement tous les accessoires hors fonction. Cette solution doit abandonner les fermetures. Néanmoins, il est toujours bon de savoir et de réaliser Pour plus d'informations sur React, veuillez consulter notre cours "React the ES6 Way". Cet article a été évalué par des pairs par Jack Franklin. Merci à tous les pair-évaluateurs de SitePoint pour avoir rendu le contenu de SitePoint parfait! Les questions fréquemment posées sur l'optimisation des performances de la réaction avec les composants sans état Les composants d'état (également appelés composants de classe) maintiennent la mémoire des changements de l'état des composants au fil du temps. Ils sont responsables de la façon dont les composants se comportent et rendent. D'un autre côté, les composants apatrides (également appelés composants fonctionnels) n'ont pas leur propre état. Ils reçoivent des données du composant parent sous forme d'accessoires et les rendent. Ils sont principalement responsables de la partie UI de la composante. Les composants apatrides peuvent considérablement améliorer les performances des applications React. Puisqu'ils ne gèrent pas leurs propres méthodes d'état ou de cycle de vie, ils ont moins de code, ce qui les rend plus faciles à comprendre et à tester. Ils peuvent également réduire la quantité de mémoire consommée par l'application. Pour optimiser les performances, vous pouvez convertir autant que possible les composants d'état en composants sans état et utiliser REACT La conversion d'un composant d'état en un composant sans état comprend la suppression de toute méthode d'état ou de cycle de vie du composant. Le composant ne doit recevoir que des données par le biais d'accessoires et les rendre. Voici un exemple: // Composant d'état
La classe Welcome étend React.
rendre() {
return // Composant sans état
fonction bienvenue (accessoires) {
return Certaines meilleures pratiques pour l'utilisation de composants sans état dans REACT: Utilisez autant que possible pour améliorer les performances et la lisibilité; Il est également recommandé d'utiliser des accessoires de déconstruction pour écrire du code de nettoyage. Non, les composants apatrides ne peuvent pas utiliser les méthodes de cycle de vie dans React. Les méthodes de cycle de vie ne sont applicables qu'aux composants de classe. Cependant, avec l'introduction de crochets dans React 16.8, vous pouvez désormais ajouter des fonctionnalités d'état et de cycle de vie aux composants de fonction. Bien que les composants apatrides présentent de nombreux avantages, ils ont également certaines limites. Ils ne peuvent pas utiliser les méthodes de cycle de vie ou gérer leur propre état. Cependant, ces limitations peuvent être surmontées en utilisant des crochets React. Les composants apatrides améliorent la lisibilité du code grâce à la simplicité et à la clarté. Ils ne gèrent pas leur propre état ou n'utilisent pas de méthodes de cycle de vie, ce qui les rend plus simples et plus faciles à comprendre. Ils encouragent également l'utilisation de petits composants réutilisables, ce qui peut rendre le code plus organisé et plus facile à entretenir. Oui, les composants apatrides peuvent gérer les événements dans React. Les gestionnaires d'événements peuvent être adoptés sous forme d'accessoires aux composants apatrides. Voici un exemple: fonction actionLink (accessoires) {
Retour (
Cliquez sur moi
));
} Les composants apatrides favorisent la modularisation du code en favorisant l'utilisation de petits composants réutilisables. Chaque composant peut être développé et testé indépendamment, ce qui rend le code plus facile à maintenir et à comprendre. Il encourage également la séparation des préoccupations, où chaque composant est responsable d'une seule fonction. Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!import React, { Component } from 'react'
class User extends Component {
render() {
const { name, highlighted, userSelected } = this.props
console.log('Hey User is being rendered for', [name, highlighted])
return <div>
<h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
onClick={event => {
userSelected()
}}>
{name}
</h3>
</div>
}
}
const User = ({ name, highlighted, userSelected }) => {
console.log('Hey User is being rendered for', [name, highlighted])
return <div>
<h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
onClick={event => {
userSelected()
}}>
{name}
</h3>
</div>
}
shallowEqual
à l'identification "automatiquement" de l'entrée et de la comparaison est une fonction, et simplement parce que Cela n'est pas égal ne signifie pas qu'il est réellement différent. recompose.onlyUpdateForKeys
en cas de besoin.
Quelle est la principale différence entre un composant avec état et un composant apatride dans React?
Comment optimiser les performances de mon application React à l'aide de composants sans état?
PureComponent
pour éviter une rediffusion inutile. Qu'est-ce que PureComponent et comment aide-t-il à optimiser les performances de réaction?
PureComponent
est un composant REACT spécial qui aide à optimiser les performances de votre application. Il utilise une comparaison peu profonde de l'hélice et de l'état pour implémenter la méthode du cycle de vie shouldComponentUpdate
. Cela signifie qu'il ne renvoiera que lorsque l'État ou les accessoires changent, ce qui peut réduire considérablement le redémarrage inutile et améliorer les performances. Comment convertir un composant d'état dans REACT en un composant apatride?
Quelles sont les meilleures pratiques pour utiliser des composants sans état dans React?
Les composants apatrides peuvent-ils utiliser des méthodes de cycle de vie dans React?
Quelles sont les limites des composants apatrides en réaction?
Comment les composants apatrides peuvent-ils améliorer la lisibilité du code?
Les composants apatrides peuvent-ils gérer les événements dans React?
Comment les composants sans état favorisent-ils la modularisation du code?