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>