Большая производительность списка с React
Я нахожусь в процессе реализации фильтруемого списка с React. Структура списка показана на рисунке ниже.
ПОМЕЩЕНИЕ
Вот описание того, как это должно работать:
Состояние находится в компоненте самого высокого уровня,Search
составная часть.Состояние описывается следующим образом:{ visible : boolean, files : array, filtered : array, query : string, currentlySelectedIndex : integer }
files
потенциально очень большой массив, содержащий пути к файлам (10000 записей - вероятное число).filtered
фильтруемый массив после того, как пользователь вводит не менее 2 символов Я знаю, что это производные данные и, как таковой аргумент может быть сделано о хранении их в состоянии, но это необходимо дляcurrentlySelectedIndex
который является индексом выбранного в данный момент элемента из отфильтрованного списка.
Пользователь вводит более 2 букв вInput
компонент, массив фильтруется, и для каждой записи в фильтруемом массиве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
Решение очень похоже на предложенное Аскаровым Бекнаромответниже
ОБНОВЛЕНИЕ 14-4-2018
Поскольку этот вопрос, по всей видимости, является популярным, и с тех пор, как был задан первоначальный вопрос, дела продвигаются, но я призываю вас посмотреть видео, указанное выше, чтобы понять виртуальный макет, я также призываю вас использоватьРеагировать виртуализировано библиотека, если вы не хотите заново изобретать колесо.