web-dev-qa-db-fra.com

Pourquoi les accessoires JSX ne devraient-ils pas utiliser des fonctions de flèche ou une liaison?

Je lance des peluches avec mon application React et je reçois cette erreur:

error    JSX props should not use arrow functions        react/jsx-no-bind

Et c’est là que je lance la fonction de flèche (dans onClick):

{this.state.photos.map(tile => (
  <span key={tile.img}>
    <Checkbox
      defaultChecked={tile.checked}
      onCheck={() => this.selectPicture(tile)}
      style={{position: 'absolute', zIndex: 99, padding: 5, backgroundColor: 'rgba(255, 255, 255, 0.72)'}}
    />
    <GridTile
      title={tile.title}
      subtitle={<span>by <b>{tile.author}</b></span>}
      actionIcon={<IconButton onClick={() => this.handleDelete(tile)}><Delete color="white"/></IconButton>}
    >
      <img onClick={() => this.handleOpen(tile.img)} src={tile.img} style={{cursor: 'pointer'}}/>
    </GridTile>
  </span>
))}

Est-ce une mauvaise pratique à éviter? Et quelle est la meilleure façon de le faire?

71
KadoBOT

Pourquoi vous ne devriez pas utiliser de fonctions de flèche intégrées dans les accessoires JSX

L'utilisation de fonctions fléchées ou la liaison dans JSX est une mauvaise pratique qui nuit aux performances, car la fonction est recréée à chaque rendu.

  1. Chaque fois qu'une fonction est créée, la fonction précédente est nettoyée. Le rendu de nombreux éléments peut créer des janks dans les animations.

  2. L'utilisation d'une fonction de flèche en ligne entraîne PureComponents, ainsi que les composants qui utilisent shallowCompare dans la méthode shouldComponentUpdate pour restituer le rendu. Etant donné que la fonction flèche est recréée à chaque fois, la comparaison superficielle l'identifiera comme une modification apportée à un accessoire et le composant sera rendu.

Comme vous pouvez le voir dans les 2 exemples suivants - lorsque nous utilisons la fonction de flèche inline, le composant <Button> est rendu à chaque fois (la console affiche le texte du bouton "rendu").

Exemple 1 - PureComponent sans gestionnaire en ligne}

class Button extends React.PureComponent {
  render() {
    const { onClick } = this.props;
    
    console.log('render button');
    
    return (
      <button onClick={ onClick }>Click</button>
    );
  }
}

class Parent extends React.Component {
  state = {
    counter: 0
  }
  
  onClick = () => this.setState((prevState) => ({
    counter: prevState.counter + 1
  }));
  
  render() {
    const { counter } = this.state;
    
    return (
      <div>
        <Button onClick={ this.onClick } />
        <div>{ counter }</div>
      </div>
    );
  }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Exemple 2 - PureComponent avec le gestionnaire en ligne}

class Button extends React.PureComponent {
  render() {
    const { onClick } = this.props;
    
    console.log('render button');
    
    return (
      <button onClick={ onClick }>Click</button>
    );
  }
}

class Parent extends React.Component {
  state = {
    counter: 0
  }
  
  render() {
    const { counter } = this.state;
    
    return (
      <div>
        <Button onClick={ () => this.setState((prevState) => ({
          counter: prevState.counter + 1
        })) } />
        <div>{ counter }</div>
      </div>
    );
  }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Méthodes de liaison à this sans les fonctions de flèche en ligne

  1. Liaison manuelle de la méthode dans le constructeur:

    class Button extends React.Component {
      constructor(props, context) {
        super(props, context);
    
        this.cb = this.cb.bind(this);
      }
    
      cb() {
    
      }
    
      render() {
        return (
          <button onClick={ this.cb }>Click</button>
        );
      }
    }
    
  2. Liaison d’une méthode à l’aide de proposition-classe-champs avec une fonction de flèche. S'agissant d'une proposition d'étape 3, vous devez ajouter le préréglage Etape 3 ou les propriétés de Classe - à votre configuration babel.

    class Button extends React.Component {
      cb = () => { // the class property is initialized with an arrow function that binds this to the class
    
      }
    
      render() {
        return (
          <button onClick={ this.cb }>Click</button>
        );
      }
    }
    
126
Ori Drori

En effet, une fonction de flèche créera apparemment une nouvelle instance de la fonction à chaque rendu si elle est utilisée dans une propriété JSX. Cela risque de créer une charge énorme sur le ramasse-miettes et d'empêcher le navigateur d'optimiser les "chemins chauds", car les fonctions seront supprimées au lieu d'être réutilisées.

Vous pouvez voir l'explication complète et quelques informations supplémentaires à https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md

8

Pour éviter de créer de nouvelles fonctions avec les mêmes arguments, vous pouvez mémoriser le résultat de la liaison de la fonction. Voici un utilitaire simple nommé memobind: ( https://github.com/supnate/memobind

4
supNate

Vous pouvez utiliser les fonctions de flèche à l'aide de react-cached-handler library, vous n'avez pas à vous soucier des performances de restitution:

Remarque: en interne, vos fonctions de flèche sont mises en cache par la clé spécifiée. Plus besoin de vous inquiéter de la restitution!

render() {

  return <div>
  {
        this.props.photos.map(photo=>
          <Photo key={photo.url}
            onClick={this.handler(photo.url, (url) => { 
                 console.log(url) })}
          />)
   }
 </div>

}

Autres fonctionnalités:

  • Gestionnaires nommés
  • Gérer les événements par les fonctions de flèche
  • Accès à la clé, aux arguments personnalisés et à l'événement d'origine
  • Performance de rendu de composant
  • Contexte personnalisé pour les gestionnaires
0
Ghominejad

Utiliser des fonctions en ligne comme celle-ci convient parfaitement. La règle anti-peluches est obsolète. 

Cette règle date d'une époque où les fonctions de flèche n'étaient pas aussi communes et où les gens utilisaient .bind (this), qui était lent auparavant. Le problème de performances a été corrigé dans Chrome 49.

Veillez à ne pas transmettre les fonctions en ligne comme accessoires à un composant enfant.

Ryan Florence, l'auteur de React Router, a écrit un excellent article à ce sujet:

https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578

0
sbaechler