Большая производительность списка с 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

Поскольку этот вопрос, по всей видимости, является популярным, и с тех пор, как был задан первоначальный вопрос, дела продвигаются, но я призываю вас посмотреть видео, указанное выше, чтобы понять виртуальный макет, я также призываю вас использоватьРеагировать виртуализировано библиотека, если вы не хотите заново изобретать колесо.

Ответы на вопрос(8)

Ваш ответ на вопрос