Utilisation correcte des fonctions de flèche dans React
je suis en utilisant ReactJS avec Babel et Webpack et à l'aide de ES6 ainsi que l' champs de classe proposés pour les fonctions de flèche. Je comprends que les fonctions de flèche rendent les choses plus efficaces par ne pas recréer les fonctions de chaque Rendu similaire au fonctionnement de la reliure dans le constructeur. Cependant, je ne suis pas sûr à 100% de les utiliser correctement. Ce qui suit est une coupe simplifiée de mon code dans trois fichiers différents.
mon code:
Main.js
prevItem = () => {
console.log("Div is clicked")
}
render(){
return (
<SecondClass prevItem={this.prevItem} />
)
}
SecondClass.js
<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />
ThirdClass.js
<div onClick={()=>{this.props.onClick()}}>Previous</div>
Question:
mon code ci-dessus utilise-t-il correctement les fonctions de flèche? J'ai remarqué que pour SecondClass.js j'aurais également pu utiliser:
<ThirdClass type="prev" onClick={this.props.prevItem} />
y a-t-il une différence entre une méthode ou l'autre puisque j'ai utilisé une fonction de flèche ES6 dans ma définition de fonction originale? Ou devrais-je utiliser la syntaxe de la flèche tout le chemin jusqu'à mon dernier div?
4 réponses
je comprends que la flèche fonctions de rendre les choses plus efficaces en a pas recréer les fonctions rend chacune similaire à la façon dont la liaison dans le constructeur œuvres.
Ce n'est pas vrai. Cela dépend de l'endroit exact où vous utilisez la fonction Flèche. Si Arrow function
sont utilisés dans la méthode render, puis ils créent une nouvelle instance everytime
render est appelé commebind
va fonctionner. Considérons cet exemple
<div onClick={()=>{this.onClick()}}>Previous</div>
Ici chaque fois que le rendu est appelé une fonction anonyme est créé et cette fonction quand appelé, appelle this.onClick
.
Toutefois de considérer le cas ci-dessous
onClick = () => {
console.log("Div is clicked")
}
dans le cas ci-dessus, la fonction flèche ne recrée pas la fonction à chaque fois, mais lie le contexte au composant React comme An arrow function does not have its own this; the this value of the enclosing execution context is used.
une fois lorsque la classe est instanciée. Ceci est similaire à la façon dont binding works is constructor
. C'est une partie de proposed class fields for arrow functions
et ce n'est pas un ES6 fonctionnalité,
pour comprendre ce que vous voulez demander, vous devez savoir qu'une fonction est son contexte d'où il est appelé. Vérifier this question
pour plus de compréhension.
Dans votre cas, vous avez utilisé Arrow function
définir prevItem
et donc il obtient le contexte du composant de réaction d'enclos.
prevItem = () => {
console.log("Div is clicked")
}
render(){
return (
<SecondClass prevItem={this.prevItem} />
)
}
Maintenant, dans son enfant, même si vous appelez prevItem
avec n'importe quel contexte personnalisé, using bind or arrow function
,prevItem
lorsqu'il est exécuté dans parent I. e Main.js
va obtenir le contexte de son composant de réaction d'enclos. Et puisque vous voulez simplement exécutez la fonction prevItem et ne voulez pas transmettre de données à celle-ci de la part de l'enfant, en écrivant
<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />
et
<div onClick={()=>{this.props.onClick()}}>Previous</div>
est tout simplement inutile et ne fera qu'Ajouter aux implications de performance puisque de nouvelles fonctions sont créées dans SecondClass
et ThirdClass
à chaque fois. Vous n'avez tout simplement pas besoin d'avoir ces fonctions définies comme une fonction de flèche et vous pouvez simplement écrire
<ThirdClass type="prev" onClick={this.props.prevItem} />
et
<div onClick={this.props.onClick}>Previous</div>
puisqu'il est déjà fixé dans le parent.
maintenant même si vous devez passer quelques données supplémentaires à ces fonctions de ThirdClass et SecondClass, vous ne devriez pas utiliser directement Arrow function
ou bind in render
. Jetez un oeil à cette réponse sur How to Avoid binding in Render method
je comprends que les fonctions de flèche rendent les choses plus efficaces en ne recréer les fonctions à chaque fois qu'ils sont appelés
les fonctions Arrow gèrent le this
dans un contexte lexical de manière "normale" est la fonction dynamiquement.
sur vos deux exemples de la fonction flèche en ligne, vous créez une nouvelle instance de fonction render
.
Cela créera et passera une nouvelle instance sur chaque Rendu
onClick={() => {}}
dans le troisième exemple, vous n'avez qu'une seule instance.
Cela ne passer une référence à une instance de
onClick={this.myHandler}
Quant aux avantages des fonctions de flèche en tant que champs de classe (il y a un petite mise de côté, je le posterai au bas de la réponse), si vous avez un gestionnaire de fonction normal qui doit accédez à l'instance courante du
class
par this
:
myHandler(){
// this.setState(...)
}
Vous devez explicite bind
la class
.
L'approche la plus courante sera de le faire dans le constructor
parce qu'il s'exécute une seule fois:
constructor(props){
super(props);
this.myHandler = this.myHandler.bind(this);
}
Si vous utilisez une flèche fonction comme gestionnaire, vous n'avez pas besoin de bind
la class
car comme mentionné ci-dessus, la fonction arrow utilise un contexte lexical pour this
:
myHandler = () => {
// this.setState(...)
}
Avec les deux les approches que vous allez utiliser le gestionnaire comme ceci:
<div onClick={this.myHandler}></div>
La raison principale de cette approche:
<div onClick={() => this.myHandler(someParameter)}></div>
si vous voulez passer des paramètres au gestionnaire de côté le natif event
qui sont passés, ce qui signifie que vous voulez passer un paramètre vers le haut.
comme mentionné, cela créera une nouvelle instance de fonction sur chaque Rendu.
(Il y est une meilleure approche pour cela, continuez à lire).
exemple pour ces cas d'utilisation:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [{ name: 'item 1', active: false }, { name: 'item 2', active: true }],
}
}
toggleITem = (itemName) => {
this.setState(prev => {
const nextState = prev.items.map(item => {
if (item.name !== itemName) return item;
return {
...item,
active: !item.active
}
});
return { items: nextState };
});
}
render() {
const { items } = this.state;
return (
<div>
{
items.map(item => {
const style = { color: item.active ? 'green' : 'red' };
return (
<div
onClick={() => this.toggleITem(item.name)}
style={style}
>
{item.name}
</div>
)})
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
une meilleure approche serait de créer la composition des composants.
Vous pouvez créer un composant enfant qui encapsule la balise pertinente, aura son propre gestionnaire et obtenir à la fois l' data
et handler
comme accessoire du parent.
le composant enfant va alors invoquer le handler qu'il a reçu du parent et passer le data
comme paramètre.
exemple D'exécution avec enfant composant:
class Item extends React.Component {
onClick = () => {
const { onClick, name } = this.props;
onClick(name);
}
render() {
const { name, active } = this.props;
const style = { color: active ? 'green' : 'red' };
return (<div style={style} onClick={this.onClick}>{name}</div>)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [{ name: 'item 1', active: false }, { name: 'item 2', active: true }],
}
}
toggleITem = (itemName) => {
this.setState(prev => {
const nextState = prev.items.map(item => {
if (item.name !== itemName) return item;
return {
...item,
active: !item.active
}
});
return { items: nextState };
});
}
render() {
const { items } = this.state;
return (
<div>
{
items.map(item => {
return <Item {...item} onClick={this.toggleITem} />
})
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Champs de la Classe de l' -:
Comme je l'ai mentionné, il y a un petit côté pour les champs de classe.
La différence entre une méthode de classe et un champ de classe est que le champ de classe est attaché au instance
class
(fonction constructeur).
où comme les méthodes de classe et les objets sont attachés à prototype.
Donc, si vous avez ridiculement grande quantité d'instances de cette classe vous obtenir un résultat de performance.
étant donné ce bloc de code:
class MyClass {
myMethod(){}
myOtherMethod = () => {}
}
babel Le transposera à ceci:
var _createClass = function() {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function(Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var MyClass = function() {
function MyClass() {
_classCallCheck(this, MyClass);
this.myOtherMethod = function() {};
}
_createClass(MyClass, [{
key: "myMethod",
value: function myMethod() {}
}]);
return MyClass;
}();
donc votre première approche
<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />
vous pouvez passer tous les arguments disponibles dans ThirdClass à la fonction prevItem. C'est la bonne façon d'appeler les fonctions parent avec des arguments.Comme ceci
<ThirdClass type="prev" onClick={()=>this.props.prevItem(firstArgument, secondArgument)} />
Votre deuxième approche est
<ThirdClass type="prev" onClick={this.props.prevItem} />
cette approche vous interdit de passer tous les arguments spécifiques de ThirdClass.
les deux répartitions sont correctes, c'est juste que, cela dépend de votre utilisation cas. Les deux approche à l'aide de la fonction Flèche es6 et sont à droite dans le mentionné ci-dessus scénarios correspondants
L'utilisation de flèches dans la définition de votre fonction d'origine vous permet de ne pas lier la fonction dans votre constructeur.
si vous n'avez pas utilisé de flèche...
prevItem(){
console.log("Div is clicked")
}
alors vous devez créer un constructeur pour l'y lier...
class MyComponent extends Component {
constructor(props) {
super(props)
this.prevItem = this.prevItem.bind(this)
}
prevItem() { ... }
}
utiliser la flèche est plus facile quand vous commencez à propos parce que cela fonctionne Juste et vous n'avez pas à comprendre ce qu'est un constructeur et plonger dans les complexités de this
en javascript.
Cependant, du point de vue des performances, il est préférable de lier le constructeur. La méthode bind dans le constructeur va créer une instance unique de la fonction et la réutiliser, même si la méthode render est appelée plusieurs fois.