Warum Sie in JSX-Requisiten keine Inline-Pfeilfunktionen verwenden sollten
Die Verwendung von Pfeilfunktionen oder das Binden in JSX ist eine schlechte Vorgehensweise, die die Leistung beeinträchtigt, da die Funktion bei jedem Rendern neu erstellt wird.
Immer wenn eine Funktion erstellt wird, wird die vorherige Funktion durch Müll gesammelt. Das erneute Rendern vieler Elemente kann zu einem Ruck in Animationen führen.
Die Verwendung einer Inline- Pfeilfunktion führt dazu, dass PureComponent
s und Komponenten, die shallowCompare
in der shouldComponentUpdate
Methode verwendet werden, ohnehin neu gerendert werden. Da die Pfeilfunktionsstütze jedes Mal neu erstellt wird, wird sie beim flachen Vergleich als Änderung an einer Stütze identifiziert, und die Komponente wird erneut gerendert.
Wie Sie in den folgenden 2 Beispielen sehen können, wird die <Button>
Komponente bei Verwendung der Inline- Pfeilfunktion jedes Mal neu gerendert (auf der Konsole wird der Text der Schaltfläche "Rendern" angezeigt).
Beispiel 1 - PureComponent ohne Inline-Handler
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>
Beispiel 2 - PureComponent mit Inline-Handler
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>
Bindungsmethoden this
ohne Inlining-Pfeilfunktionen
Manuelles Binden der Methode im Konstruktor:
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>
);
}
}
Binden einer Methode unter Verwendung der Vorschlagsklassenfelder mit einer Pfeilfunktion. Da es sich um einen Vorschlag für Stufe 3 handelt, müssen Sie Ihrer Babel-Konfiguration die Voreinstellung für Stufe 3 oder die Transformation der Klasseneigenschaften hinzufügen .
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>
);
}
}
Funktionskomponenten mit inneren Rückrufen
Wenn wir eine innere Funktion (z. B. Ereignishandler) innerhalb einer Funktionskomponente erstellen, wird die Funktion jedes Mal neu erstellt, wenn die Komponente gerendert wird. Wenn die Funktion als Requisiten (oder über den Kontext) an eine untergeordnete Komponente übergeben wird ( Button
in diesem Fall), wird dieses untergeordnete Element ebenfalls neu gerendert.
Beispiel 1 - Funktionskomponente mit innerem Rückruf:
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>
Um dieses Problem zu lösen, können wir den Rückruf mit dem useCallback()
Hook umschließen und die Abhängigkeiten auf ein leeres Array setzen.
Hinweis: Die useState
generierte Funktion akzeptiert eine Updater-Funktion, die den aktuellen Status bereitstellt. Auf diese Weise müssen wir für den aktuellen Status keine Abhängigkeit von festlegen useCallback
.
Beispiel 2 - Funktionskomponente mit einem inneren Rückruf, der mit useCallback umschlossen ist:
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>
this
, daher gibt es nichts zu binden. Normalerweise werden die Methoden von einer Wrapper-Smart-Komponente bereitgestellt.onClick={() => { onTodoClick(todo.id) }
cb() { onTodoClick(this.props.todo.id); }
.useCallback
mit dynamischem Wert verwendet. stackoverflow.com/questions/55006061/…Dies liegt daran, dass eine Pfeilfunktion anscheinend bei jedem Rendern eine neue Instanz der Funktion erstellt, wenn sie in einer JSX-Eigenschaft verwendet wird. Dies kann den Garbage Collector stark belasten und den Browser daran hindern, "Hot Paths" zu optimieren, da Funktionen weggeworfen und nicht wiederverwendet werden.
Die gesamte Erklärung und weitere Informationen finden Sie unter https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
quelle
bind
schießen Sie mit oder mit den Pfeilfunktionen in den Fuß. Es ist jedoch nicht gut dokumentiert, insbesondere im Fall der Arbeit mitmap
Ping-Arrays in Listen usw.Um zu vermeiden, dass neue Funktionen mit denselben Argumenten erstellt werden, können Sie sich das Ergebnis der Funktionsbindung merken. Hier ist ein einfaches Dienstprogramm mit dem Namen
memobind
merken. https://github.com/supnate/memobindquelle
Die Verwendung solcher Inline-Funktionen ist vollkommen in Ordnung. Die Flusenregel ist veraltet.
Diese Regel stammt aus einer Zeit, in der Pfeilfunktionen nicht so häufig waren und die Benutzer .bind (this) verwendeten, was früher langsam war. Das Leistungsproblem wurde in Chrome 49 behoben.
Achten Sie darauf, dass Sie keine Inline-Funktionen als Requisiten an eine untergeordnete Komponente übergeben.
Ryan Florence, der Autor von React Router, hat ein großartiges Stück darüber geschrieben:
https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578
quelle
Sie können Pfeilfunktionen mithilfe der React-Cached-Handler- Bibliothek verwenden, ohne sich um die Leistung beim erneuten Rendern sorgen zu müssen:
Andere Eigenschaften:
quelle
Warum sollten JSX-Requisiten keine Pfeilfunktionen verwenden oder binden?
Meistens, weil Inline-Funktionen das Auswendiglernen optimierter Komponenten unterbrechen können:
Es geht weniger um zusätzliche Kosten für die Funktionserstellung:
Wann ist der
react/jsx-no-bind
Regel sinnvoll?Sie möchten sicherstellen, dass gespeicherte Komponenten wie vorgesehen funktionieren:
React.memo
(für Funktionskomponenten)PureComponent
oder benutzerdefiniertshouldComponentUpdate
(für Klassenkomponenten)Unter Einhaltung dieser Regel werden stabile Funktionsobjektreferenzen übergeben. Daher können die oben genannten Komponenten die Leistung optimieren, indem sie ein erneutes Rendern verhindern, wenn sich die vorherigen Requisiten nicht geändert haben.
Wie löse ich den ESLint-Fehler?
Klassen: Definieren Sie den Handler als Methode oder Klasseneigenschaft für die
this
Bindung.Haken: Verwenden
useCallback
.Mittelgrund
In vielen Fällen sind Inline-Funktionen sehr bequem zu bedienen und hinsichtlich der Leistungsanforderungen absolut in Ordnung. Leider kann diese Regel nicht nur auf gespeicherte Komponententypen beschränkt werden. Wenn Sie es weiterhin allgemein verwenden möchten, können Sie es beispielsweise für einfache DOM-Knoten deaktivieren :
quelle