Pourquoi vous ne devriez pas utiliser les fonctions de flèche en ligne dans les accessoires JSX
L'utilisation de fonctions fléchées ou de liaison dans JSX est une mauvaise pratique qui nuit aux performances, car la fonction est recréée à chaque rendu.
Chaque fois qu'une fonction est créée, la fonction précédente est garbage collection. Le rendu de nombreux éléments peut créer des saccades dans les animations.
L'utilisation d'une fonction de flèche en ligne provoquera le rendu de PureComponent
s et des composants utilisés shallowCompare
dans la shouldComponentUpdate
méthode. Étant donné que l'accessoire de fonction de flèche est recréé à chaque fois, la comparaison superficielle l'identifiera comme une modification apportée à un accessoire et le composant sera renvoyé.
Comme vous pouvez le voir dans les 2 exemples suivants - lorsque nous utilisons la fonction de flèche en ligne, le <Button>
composant est rendu à chaque fois (la console affiche le texte du 'bouton de 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 un 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>
Liaison de méthodes à des this
fonctions fléchées sans insertion
Lier la méthode manuellement 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>
);
}
}
Lier une méthode à l'aide des champs de classe de proposition avec une fonction de flèche. Comme il s'agit d'une proposition de l'étape 3, vous devrez ajouter le préréglage de l' étape 3 ou la transformation des 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>
);
}
}
Composants de fonction avec rappels internes
Lorsque nous créons une fonction interne (gestionnaire d'événements par exemple) à l'intérieur d'un composant de fonction, la fonction sera recréée à chaque fois que le composant est rendu. Si la fonction est passée en tant qu'accessoires (ou via le contexte) à un composant enfant ( Button
dans ce cas), cet enfant sera également rendu.
Exemple 1 - Composant de fonction avec un rappel interne:
const { memo, useState } = React;
const Button = memo(({ onClick }) => console.log('render button') || (
<button onClick={onClick}>Click</button>
));
const Parent = () => {
const [counter, setCounter] = useState(0);
const increment = () => setCounter(counter => counter + 1); // the function is recreated all the time
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
Pour résoudre ce problème, nous pouvons encapsuler le rappel avec le useCallback()
hook et définir les dépendances sur un tableau vide.
Remarque: la useState
fonction générée accepte une fonction de mise à jour, qui fournit l'état actuel. De cette façon, nous n'avons pas besoin de définir l'état actuel comme une dépendance de useCallback
.
Exemple 2 - Composant de fonction avec un rappel interne enveloppé avec useCallback:
const { memo, useState, useCallback } = React;
const Button = memo(({ onClick }) => console.log('render button') || (
<button onClick={onClick}>Click</button>
));
const Parent = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(() => setCounter(counter => counter + 1), []);
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>