Tôi đang trong quá trình triển khai danh sách có thể lọc với React. Cấu trúc của danh sách như trong hình dưới đây.
LỜI NÓI ĐẦU
Dưới đây là mô tả về cách nó hoạt động:
- Trạng thái nằm trong thành phần cấp cao nhất,
Search
thành phần. - Trạng thái được mô tả như sau:
{ hiển thị: boolean, tệp: mảng, lọc: mảng, chuỗi truy vấn, hiệnSelectedIndex: số nguyên }
files
là một mảng có khả năng rất lớn, chứa các đường dẫn tệp (10000 mục nhập là một con số hợp lý).filtered
là mảng được lọc sau khi người dùng nhập ít nhất 2 ký tự. Tôi biết đó là dữ liệu phái sinh và như vậy có thể lập luận về việc lưu trữ nó ở trạng thái nhưng nó cần thiết đểcurrentlySelectedIndex
là chỉ mục của phần tử hiện được chọn từ danh sách đã lọc.Người dùng nhập nhiều hơn 2 chữ cái vào
Input
thành phần, mảng được lọc và đối với mỗi mục nhập trong mảng được lọc, mộtResult
thành phần được hiển thịMỗi
Result
thành phần đang hiển thị đường dẫn đầy đủ khớp một phần với truy vấn và phần khớp một phần của đường dẫn được đánh dấu. Ví dụ: DOM của một thành phần Kết quả, nếu người dùng đã nhập 'le' sẽ giống như sau:<li>this/is/a/fi<strong>le</strong>/path</li>
- Nếu người dùng nhấn các phím lên hoặc xuống trong khi
Input
thành phần được lấy nét, cáccurrentlySelectedIndex
thay đổi dựa trênfiltered
mảng. Điều này khiếnResult
thành phần phù hợp với chỉ mục được đánh dấu là đã chọn gây ra kết xuất lại
VẤN ĐỀ
Ban đầu, tôi đã thử nghiệm điều này với một mảng đủ nhỏ files
, sử dụng phiên bản phát triển của React và tất cả đều hoạt động tốt.
Sự cố xuất hiện khi tôi phải xử lý một files
mảng lớn tới 10000 mục nhập. Việc gõ 2 chữ cái vào Input sẽ tạo ra một danh sách lớn và khi tôi nhấn phím lên và xuống để điều hướng nó sẽ rất chậm.
Lúc đầu, tôi không có một thành phần xác định cho các Result
phần tử và tôi chỉ đang lập danh sách một cách nhanh chóng, trên mỗi lần hiển thị của Search
thành phần, chẳng hạn như:
results = this.state.filtered.map(function(file, index) {
var start, end, matchIndex, match = this.state.query;
matchIndex = file.indexOf(match);
start = file.slice(0, matchIndex);
end = file.slice(matchIndex + match.length);
return (
<li onClick={this.handleListClick}
data-path={file}
className={(index === this.state.currentlySelected) ? "valid selected" : "valid"}
key={file} >
{start}
<span className="marked">{match}</span>
{end}
</li>
);
}.bind(this));
Như bạn có thể nói, mỗi lần currentlySelectedIndex
thay đổi, nó sẽ tạo ra một kết xuất lại và danh sách sẽ được tạo lại mỗi lần. Tôi nghĩ rằng vì tôi đã đặt một key
giá trị trên mỗi li
phần tử nên React sẽ tránh hiển thị mọi li
phần tử khác không có sự className
thay đổi của nó , nhưng rõ ràng là không phải như vậy.
Tôi đã kết thúc việc xác định một lớp cho các Result
phần tử, nơi nó kiểm tra rõ ràng xem mỗi Result
phần tử có nên hiển thị lại hay không dựa trên việc liệu nó đã được chọn trước đó hay chưa và dựa trên đầu vào của người dùng hiện tại:
var ResultItem = React.createClass({
shouldComponentUpdate : function(nextProps) {
if (nextProps.match !== this.props.match) {
return true;
} else {
return (nextProps.selected !== this.props.selected);
}
},
render : function() {
return (
<li onClick={this.props.handleListClick}
data-path={this.props.file}
className={
(this.props.selected) ? "valid selected" : "valid"
}
key={this.props.file} >
{this.props.children}
</li>
);
}
});
Và danh sách bây giờ được tạo như sau:
results = this.state.filtered.map(function(file, index) {
var start, end, matchIndex, match = this.state.query, selected;
matchIndex = file.indexOf(match);
start = file.slice(0, matchIndex);
end = file.slice(matchIndex + match.length);
selected = (index === this.state.currentlySelected) ? true : false
return (
<ResultItem handleClick={this.handleListClick}
data-path={file}
selected={selected}
key={file}
match={match} >
{start}
<span className="marked">{match}</span>
{end}
</ResultItem>
);
}.bind(this));
}
Điều này làm cho hiệu suất tốt hơn một chút , nhưng nó vẫn chưa đủ tốt. Điều đó là khi tôi thử nghiệm trên phiên bản sản xuất của React, mọi thứ hoạt động rất mượt mà, không có độ trễ nào cả.
ĐÁP ÁN
Sự khác biệt đáng chú ý như vậy giữa các phiên bản phát triển và sản xuất của React có bình thường không?
Tôi có hiểu / đang làm gì sai khi nghĩ về cách React quản lý danh sách không?
CẬP NHẬT 14-11-2016
Tôi đã tìm thấy bài thuyết trình này của Michael Jackson, nơi anh ấy giải quyết một vấn đề rất giống với bài thuyết trình này: https://youtu.be/7S8v8jfLb1Q?t=26m2s
Giải pháp rất giống với giải pháp được đề xuất bởi câu trả lời của AskarovBeknar , bên dưới
CẬP NHẬT 14-4-2018
Vì đây rõ ràng là một câu hỏi phổ biến và mọi thứ đã tiến triển kể từ khi câu hỏi ban đầu được hỏi, trong khi tôi khuyến khích bạn xem video được liên kết ở trên, để nắm bắt được bố cục ảo, tôi cũng khuyến khích bạn sử dụng React Virtualized thư viện nếu bạn không muốn phát minh lại bánh xe.