Tại sao bạn không nên sử dụng các hàm mũi tên nội tuyến trong các đạo cụ JSX
Sử dụng các hàm mũi tên hoặc liên kết trong JSX là một phương pháp không tốt làm ảnh hưởng đến hiệu suất, vì hàm được tạo lại trên mỗi lần hiển thị.
Bất cứ khi nào một hàm được tạo, hàm trước đó sẽ được thu gom. Việc kết xuất nhiều phần tử có thể tạo ra sự xáo trộn trong hoạt ảnh.
Việc sử dụng hàm mũi tên nội tuyến sẽ khiến PureComponent
s và các thành phần sử dụng shallowCompare
trong shouldComponentUpdate
phương thức vẫn hiển thị. Vì hỗ trợ hàm mũi tên được tạo lại mỗi lần, so sánh nông sẽ xác định nó là một thay đổi đối với hỗ trợ và thành phần sẽ hiển thị.
Như bạn có thể thấy trong 2 ví dụ sau - khi chúng tôi sử dụng hàm mũi tên nội tuyến, <Button>
thành phần được hiển thị mỗi lần (bảng điều khiển hiển thị văn bản 'nút kết xuất').
Ví dụ 1 - PureComponent không có trình xử lý nội tuyến
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>
Ví dụ 2 - PureComponent với trình xử lý nội tuyến
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>
Các phương thức ràng buộc this
không có hàm mũi tên nội tuyến
Ràng buộc phương thức theo cách thủ công trong hàm tạo:
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>
);
}
}
Ràng buộc một phương thức bằng cách sử dụng các trường -lớp-đề xuất với một hàm mũi tên. Vì đây là đề xuất ở giai đoạn 3, bạn sẽ cần thêm cài đặt trước Giai đoạn 3 hoặc biến đổi thuộc tính Lớp vào cấu hình babel của mình.
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>
);
}
}
Các thành phần chức năng với các lệnh gọi lại bên trong
Khi chúng ta tạo một hàm bên trong (ví dụ: trình xử lý sự kiện) bên trong một thành phần hàm, hàm sẽ được tạo lại mỗi khi thành phần đó được hiển thị. Nếu hàm được chuyển dưới dạng đạo cụ (hoặc thông qua ngữ cảnh) cho một thành phần con ( Button
trong trường hợp này), thì thành phần con đó cũng sẽ hiển thị lại.
Ví dụ 1 - Thành phần hàm có lệnh gọi lại bên trong:
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>
Để giải quyết vấn đề này, chúng ta có thể bao bọc cuộc gọi lại bằng useCallback()
hook và đặt các phụ thuộc thành một mảng trống.
Lưu ý: các useState
chức năng được tạo ra chấp nhận một chức năng cập nhật, cung cấp tình trạng hiện thời. Bằng cách này, chúng ta không cần đặt trạng thái hiện tại làm phụ thuộc useCallback
.
Ví dụ 2 - Thành phần hàm với một lệnh gọi lại bên trong được bao bọc bởi 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>