React avec ES6+
Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →
Lors de la refonte complète d'Instagram Web cette année, nous avons apprécié utiliser plusieurs fonctionnalités ES6+ pour écrire nos composants React. Permettez-moi de souligner comment ces nouvelles fonctionnalités langagières peuvent transformer votre façon d'écrire une application React, la rendant plus simple et plus agréable que jamais.
Classes
De loin, le changement le plus visible dans notre façon d'écrire des composants React avec ES6+ survient lorsque nous choisissons d'utiliser la syntaxe de définition de classe. Plutôt que d'employer la méthode React.createClass pour définir un composant, nous pouvons créer une véritable classe ES6 qui étend React.Component :
class Photo extends React.Component {
render() {
return <img alt={this.props.caption} src={this.props.src} />;
}
}
Vous remarquerez immédiatement une différence subtile – une syntaxe plus concise est désormais disponible pour définir des classes :
// The ES5 way
var Photo = React.createClass({
handleDoubleTap: function(e) { … },
render: function() { … },
});
// The ES6+ way
class Photo extends React.Component {
handleDoubleTap(e) { … }
render() { … }
}
Concrètement, nous supprimons deux parenthèses et un point-virgule final, et pour chaque méthode déclarée, nous omettons deux-points, le mot-clé function, et une virgule.
Toutes les méthodes de cycle de vie sauf une peuvent être définies comme prévu avec la nouvelle syntaxe de classe. Le constructor de la classe assume désormais le rôle précédemment dévolu à componentWillMount :
// The ES5 way
var EmbedModal = React.createClass({
componentWillMount: function() { … },
});
// The ES6+ way
class EmbedModal extends React.Component {
constructor(props) {
super(props);
// Operations usually carried out in componentWillMount go here
}
}
Initialiseurs de propriétés
Dans l'univers des classes ES6+, les types de props et leurs valeurs par défaut résident dans des propriétés statiques de la classe elle-même. Celles-ci, ainsi que l'état initial du composant, peuvent être définis via les initialiseurs de propriétés ES7 :
// The ES5 way
var Video = React.createClass({
getDefaultProps: function() {
return {
autoPlay: false,
maxLoops: 10,
};
},
getInitialState: function() {
return {
loopsRemaining: this.props.maxLoops,
};
},
propTypes: {
autoPlay: React.PropTypes.bool.isRequired,
maxLoops: React.PropTypes.number.isRequired,
posterFrameSrc: React.PropTypes.string.isRequired,
videoSrc: React.PropTypes.string.isRequired,
},
});
// The ES6+ way
class Video extends React.Component {
static defaultProps = {
autoPlay: false,
maxLoops: 10,
}
static propTypes = {
autoPlay: React.PropTypes.bool.isRequired,
maxLoops: React.PropTypes.number.isRequired,
posterFrameSrc: React.PropTypes.string.isRequired,
videoSrc: React.PropTypes.string.isRequired,
}
state = {
loopsRemaining: this.props.maxLoops,
}
}
Les initialiseurs de propriétés ES7 opèrent dans le constructeur de la classe, où this fait référence à l'instance en cours de construction. Ainsi, l'état initial peut toujours dépendre de this.props. Notamment, nous n'avons plus besoin de définir les valeurs par défaut des props et l'objet d'état initial via une fonction accesseur.
Fonctions fléchées
La méthode React.createClass effectuait un travail de liaison supplémentaire sur les méthodes d'instance de votre composant pour garantir que le mot-clé this à l'intérieur de celles-ci réfère à l'instance du composant concerné.
// Autobinding, brought to you by React.createClass
var PostInfo = React.createClass({
handleOptionsButtonClick: function(e) {
// Here, 'this' refers to the component instance.
this.setState({showOptionsModal: true});
},
});
Puisque nous n'utilisons pas la méthode React.createClass lors de la définition de composants avec la syntaxe de classe ES6+, il semblerait que nous devions lier manuellement les méthodes d'instance partout où nous voulons ce comportement :
// Manually bind, wherever you need to
class PostInfo extends React.Component {
constructor(props) {
super(props);
// Manually bind this method to the component instance...
this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
}
handleOptionsButtonClick(e) {
// ...to ensure that 'this' refers to the component instance here.
this.setState({showOptionsModal: true});
}
}
Heureusement, en combinant deux fonctionnalités ES6+ – les fonctions fléchées et les initialiseurs de propriétés – la liaison optionnelle à l'instance du composant devient un jeu d'enfant :
class PostInfo extends React.Component {
handleOptionsButtonClick = (e) => {
this.setState({showOptionsModal: true});
}
}
Le corps des fonctions fléchées ES6 partage le même this lexical que le code environnant, ce qui produit le résultat souhaité grâce au mode de portée des initialiseurs de propriétés ES7. Jetez un œil sous le capot pour comprendre pourquoi cela fonctionne.
Noms de propriétés dynamiques & littéraux de modèle
L'une des améliorations apportées aux littéraux objets inclut la capacité d'assigner un nom de propriété dérivé. Nous aurions pu initialement faire ceci pour définir un état :
var Form = React.createClass({
onChange: function(inputName, e) {
var stateToSet = {};
stateToSet[inputName + 'Value'] = e.target.value;
this.setState(stateToSet);
},
});
Désormais, nous pouvons construire des objets dont les noms de propriétés sont déterminés par une expression JavaScript à l'exécution. Ici, nous utilisons une chaîne de caractères template pour déterminer quelle propriété définir dans l'état :
class Form extends React.Component {
onChange(inputName, e) {
this.setState({
[`${inputName}Value`]: e.target.value,
});
}
}
Déstructuration et attributs spread
Souvent lors de la composition de composants, nous souhaitons transmettre la plupart des props d'un composant parent à un composant enfant, mais pas toutes. En combinant la déstructuration ES6+ avec les attributs spread JSX, cela devient possible sans cérémonie :
class AutoloadingPostsGrid extends React.Component {
render() {
const {
className,
...others // contains all properties of this.props except for className
} = this.props;
return (
<div className={className}>
<PostsGrid {...others} />
<button onClick={this.handleLoadMoreClick}>Load more</button>
</div>
);
}
}
Nous pouvons aussi combiner les attributs spread JSX avec des attributs classiques, en profitant d'une règle de précédence simple pour implémenter des surcharges et valeurs par défaut. Cet élément acquerra la className « override » même s'il existe une propriété className dans this.props :
<div {...this.props} className="override">
…
</div>
Cet élément aura normalement la className « base » à moins qu'une propriété className dans this.props ne la surcharge :
<div className="base" {...this.props}>
…
</div>
Merci pour votre lecture
J'espère que vous apprécierez d'utiliser les fonctionnalités du langage ES6+ pour écrire du code React autant que nous. Merci à mes collègues pour leurs contributions à cet article, et un merci tout particulier à l'équipe Babel pour avoir rendu le futur accessible à tous dès aujourd'hui.