Dlaczego nie powinieneś używać wbudowanych funkcji strzałek w właściwościach JSX
Używanie funkcji strzałkowych lub powiązań w JSX to zła praktyka, która obniża wydajność, ponieważ funkcja jest odtwarzana przy każdym renderowaniu.
Za każdym razem, gdy tworzona jest funkcja, poprzednia funkcja jest usuwana z pamięci. Rrenderowanie wielu elementów może powodować szarpanie w animacjach.
Korzystanie z funkcji inline strzałki spowoduje PureComponent
S i komponenty, które Zastosowanie shallowCompare
w shouldComponentUpdate
metodzie i tak rerender. Ponieważ właściwość funkcji strzałki jest odtwarzana za każdym razem, płytkie porównanie zidentyfikuje ją jako zmianę właściwości, a komponent ponownie się wyłączy.
Jak widać na poniższych 2 przykładach - gdy używamy funkcji inline arrow, <Button>
komponent jest renderowany za każdym razem (konsola wyświetla tekst „render button”).
Przykład 1 - PureComponent bez wbudowanej obsługi
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>
Przykład 2 - PureComponent z wbudowaną obsługą
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>
Wiązanie metod this
bez wbudowanych funkcji strzałek
Ręczne wiązanie metody w konstruktorze:
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>
);
}
}
Powiązanie metody przy użyciu pól klasy propozycji z funkcją strzałki. Ponieważ jest to propozycja etapu 3, musisz dodać ustawienie wstępne etapu 3 lub transformację właściwości klasy do konfiguracji 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>
);
}
}
Komponenty funkcyjne z wewnętrznymi wywołaniami zwrotnymi
Kiedy tworzymy funkcję wewnętrzną (na przykład procedurę obsługi zdarzeń) wewnątrz komponentu funkcji, funkcja ta zostanie odtworzona za każdym razem, gdy komponent jest renderowany. Jeśli funkcja zostanie przekazana jako właściwości (lub przez kontekst) do komponentu potomnego ( Button
w tym przypadku), to dziecko również zostanie ponownie wyrenderowane.
Przykład 1 - składnik funkcji z wewnętrznym wywołaniem zwrotnym:
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>
Aby rozwiązać ten problem, możemy zawinąć wywołanie zwrotne useCallback()
zaczepem i ustawić zależności na pustą tablicę.
Uwaga:useState
funkcja generowane przyjmuje funkcję Updater, który zapewnia aktualny stan. W ten sposób nie musimy ustawiać bieżącego stanu na zależność useCallback
.
Przykład 2 - składnik funkcji z wewnętrznym wywołaniem zwrotnym opakowanym za pomocą metody 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>