Mengapa Anda tidak boleh menggunakan fungsi panah sebaris di alat peraga JSX
Menggunakan fungsi panah atau pengikatan di JSX adalah praktik buruk yang merusak performa, karena fungsi tersebut dibuat ulang pada setiap render.
Setiap kali fungsi dibuat, fungsi sebelumnya adalah sampah yang dikumpulkan. Merender ulang banyak elemen dapat membuat animasi jank.
Menggunakan fungsi panah sebaris akan menyebabkan PureComponent
s, dan komponen yang digunakan shallowCompare
dalam shouldComponentUpdate
metode tetap merender. Karena prop fungsi panah dibuat ulang setiap kali, perbandingan dangkal akan mengidentifikasinya sebagai perubahan pada prop, dan komponen akan merender ulang.
Seperti yang Anda lihat pada 2 contoh berikut - ketika kita menggunakan fungsi panah sebaris, <Button>
komponen dirender setiap kali (konsol menampilkan teks 'tombol render').
Contoh 1 - PureComponent tanpa handler inline
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>
Contoh 2 - PureComponent dengan handler inline
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>
Mengikat metode ke this
tanpa fungsi panah sebaris
Mengikat metode secara manual di 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>
);
}
}
Mengikat metode menggunakan proposal-class-fields dengan fungsi panah. Karena ini adalah proposal tahap 3, Anda perlu menambahkan preset Tahap 3 atau properti Kelas diubah ke konfigurasi babel Anda.
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>
);
}
}
Komponen Fungsi dengan panggilan balik batin
Saat kita membuat fungsi bagian dalam (penanganan peristiwa misalnya) di dalam komponen fungsi, fungsi tersebut akan dibuat ulang setiap kali komponen dirender. Jika fungsi diteruskan sebagai props (atau melalui konteks) ke komponen anak ( Button
dalam hal ini), anak itu juga akan dirender ulang.
Contoh 1 - Komponen Fungsi dengan callback batin:
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>
Untuk mengatasi masalah ini, kita bisa membungkus callback dengan useCallback()
hook , dan mengatur dependensi ke array kosong.
Catatan: yang useState
fungsi yang dihasilkan menerima fungsi updater, yang menyediakan kondisi saat ini. Dengan cara ini, kita tidak perlu menyetel ketergantungan pada status saat ini useCallback
.
Contoh 2 - Komponen Fungsi dengan callback dalam yang dibungkus dengan 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>