Desempenho de grande lista com o React

Estou no processo de implementar uma lista filtrável com o React. A estrutura da lista é como mostrado na imagem abaixo.

PREMISSA

Aqui está uma descrição de como deve funcionar:

O estado reside no componente de nível mais alto, oSearch componente.O estado é descrito da seguinte maneira:
{
    visible : boolean,
    files : array,
    filtered : array,
    query : string,
    currentlySelectedIndex : integer
}
files é uma matriz potencialmente muito grande que contém caminhos de arquivo (10000 entradas é um número plausível).filtered é a matriz filtrada após o usuário digitar pelo menos 2 caracteres. Eu sei que são dados derivados e, como tal, um argumento pode ser feito sobre o armazenamento no estado, mas é necessário para

currentlySelectedIndex que é o índice do elemento atualmente selecionado da lista filtrada.

O usuário digita mais de 2 letras noInput componente, a matriz é filtrada e, para cada entrada na matriz filtrada, umResult componente é renderizado

CadaResult O componente está exibindo o caminho completo que corresponde parcialmente à consulta e a parte de correspondência parcial do caminho é destacada. Por exemplo, o DOM de um componente Result, se o usuário digitar 'le' seria algo como isto:

<li>this/is/a/fi<strong>le</strong>/path</li>

Se o usuário pressionar as teclas para cima ou para baixo enquanto oInput componente está focado ocurrentlySelectedIndex mudanças com base nofiltered array. Isso faz com que oResult componente que corresponde ao índice a ser marcado como selecionado, causando uma nova renderização

PROBLEMA

Inicialmente, testei isso com uma matriz pequena o suficientefiles, usando a versão de desenvolvimento do React, e tudo funcionou bem.

O problema apareceu quando tive que lidar com umafiles matriz tão grande quanto 10000 entradas. Digitar duas letras na entrada geraria uma grande lista e, quando eu pressionasse as teclas para cima e para baixo para navegar, seria muito lento.

No começo, eu não tinha um componente definido para oResult elementos e eu estava apenas fazendo a lista em tempo real, em cada renderização doSearch componente, como tal:

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));

Como você pode ver, toda vez que ocurrentlySelectedIndex alterado, causaria uma nova renderização e a lista seria recriada a cada vez. Eu pensei que desde que eu tinha definido umkey valor em cadali O elemento React evitaria renderizar novamente todos os outrosli elemento que não teve suaclassName mudar, mas aparentemente não era assim.

Acabei definindo uma aula para oResult elementos, onde verifica explicitamente se cadaResult O elemento deve ser renderizado novamente com base em se ele foi selecionado anteriormente e com base na entrada atual do usuário:

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>
        );
    }
});

E a lista agora é criada como tal:

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));
}

Isso fez o desempenholevemente melhor, mas ainda não é bom o suficiente. A coisa é quando eu testei na versão de produção do React, as coisas funcionaram bem amanteigadas, sem atrasos.

LINHA INFERIOR

Uma discrepância tão perceptível entre as versões de desenvolvimento e produção do React é normal?

Estou entendendo / fazendo algo errado quando penso em como o React gerencia a lista?

ATUALIZAÇÃO 14-11-2016

Eu encontrei esta apresentação de Michael Jackson, onde ele aborda um problema muito semelhante a este:https://youtu.be/7S8v8jfLb1Q?t=26m2s

A solução é muito semelhante à proposta por AskarovBeknarresponda, abaixo

ATUALIZAÇÃO 14-4-2018

Como essa é aparentemente uma pergunta popular e as coisas progrediram desde que a pergunta original foi feita, embora eu o incentive a assistir ao vídeo link acima, a fim de obter uma idéia de um layout virtual, também o incentivo a usar oReact Virtualized biblioteca se você não deseja reinventar a roda.

questionAnswers(8)

yourAnswerToTheQuestion