Я в процесі впровадження списку, який можна фільтрувати, за допомогою React. Структура списку така, як показано на малюнку нижче.
ПРОМІСЛЯ
Ось опис того, як це має працювати:
- Стан знаходиться в компоненті найвищого рівня -
Search
компоненті. - Стан описується наступним чином:
{ видимий: логічний, файли: масив, відфільтровано: масив, запит: рядок, currentlySelectedIndex: ціле число }
files
- потенційно дуже великий масив, що містить шляхи до файлів (10000 записів - це ймовірне число).filtered
- це відфільтрований масив після того, як користувач введе принаймні 2 символи. Я знаю, що це похідні дані, і як такий аргумент можна навести аргумент щодо їх зберігання у штаті, але це потрібно дляcurrentlySelectedIndex
що є індексом поточно вибраного елемента із відфільтрованого списку.Користувач вводить у
Input
компонент більше 2 літер , масив фільтрується і для кожного запису у відфільтрованому масиві відображаєтьсяResult
компонентКожен
Result
компонент відображає повний шлях, який частково відповідає запиту, а частина шляху часткового збігу виділяється. Наприклад, DOM компонента Result, якби користувач набрав 'le', був би приблизно таким:<li>this/is/a/fi<strong>le</strong>/path</li>
- Якщо користувач натискає клавіші вгору або вниз, коли
Input
компонент фокусується,currentlySelectedIndex
зміни базуються наfiltered
масиві. Це призводить до того, щоResult
компонент, що відповідає індексу, буде позначений як вибраний, що спричинить повторне відображення
ПРОБЛЕМА
Спочатку я перевірив це з досить невеликим масивом files
, використовуючи розробницьку версію React, і все працювало нормально.
Проблема з’явилася, коли мені довелося мати справу з files
масивом розміром до 10000 записів. Введення 2-х літер у введенні створить великий список, і коли я натискаю клавіші вгору і вниз для навігації, це буде дуже відсталим.
Спочатку у мене не було визначеного компонента для Result
елементів, і я просто складав список на льоту, під час кожного візуалізації Search
компонента як такого:
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));
Як ви можете зрозуміти, щоразу, коли currentlySelectedIndex
змінене, це призведе до повторного відтворення, і список буде створюватись кожного разу. Я думав, що оскільки я встановив key
значення для кожного li
елемента, React буде уникати повторного відтворення кожного іншого li
елемента, який не className
зазнав змін, але, мабуть, це було не так.
Я в кінцевому підсумку визначив клас для Result
елементів, де він явно перевіряє, чи кожен Result
елемент повинен повторно рендерити, виходячи з того, чи був він раніше вибраний та на основі поточного вводу користувача:
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>
);
}
});
І список тепер створюється як такий:
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));
}
Це покращило продуктивність , але вона все ще недостатньо хороша. Справа в тому, що коли я тестував на виробничій версії React, все працювало згладжено, без затримок.
НИЖНЯ ЛІНІЯ
Чи є така помітна розбіжність між розробницькою та виробничою версіями React нормальною?
Я розумію / роблю щось не так, коли думаю про те, як React керує списком?
ОНОВЛЕННЯ 14-11-2016
Я знайшов цю презентацію Майкла Джексона, де він вирішує проблему, дуже подібну до цієї: https://youtu.be/7S8v8jfLb1Q?t=26m2s
Рішення дуже схожий на той , запропонований AskarovBeknar в відповідь нижче
ОНОВЛЕННЯ 14-4-2018
Оскільки це, мабуть, популярне запитання, і з того часу, як було поставлено вихідне запитання, справи просунулись, хоча я закликаю вас переглянути відео, на яке посилається вище, щоб зрозуміти віртуальний макет, я також рекомендую вам використовувати React Virtualized бібліотеки, якщо ви не хочете заново винаходити колесо.