Gran lista de rendimiento con React

Estoy en el proceso de implementar una lista filtrable con React. La estructura de la lista es como se muestra en la imagen a continuación.

PREMISA

Aquí hay una descripción de cómo se supone que funciona:

El estado reside en el componente de nivel más alto, elSearch componente.El estado se describe de la siguiente manera:
{
    visible : boolean,
    files : array,
    filtered : array,
    query : string,
    currentlySelectedIndex : integer
}
files es una matriz potencialmente muy grande que contiene rutas de archivos (10000 entradas es un número plausible).filtered es la matriz filtrada después de que el usuario escribe al menos 2 caracteres. Sé que son datos derivados y, como tal, se podría argumentar sobre el almacenamiento en el estado, pero es necesario para

currentlySelectedIndex que es el índice del elemento actualmente seleccionado de la lista filtrada.

El usuario escribe más de 2 letras en elInput componente, la matriz se filtra y para cada entrada en la matriz filtrada seResult componente se representa

CadaResult El componente muestra la ruta completa que coincide parcialmente con la consulta y se resalta la parte de coincidencia parcial de la ruta. Por ejemplo, el DOM de un componente Resultado, si el usuario hubiera escrito 'le' sería algo como esto:

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

Si el usuario presiona las teclas arriba o abajo mientrasInput componente se centra elcurrentlySelectedIndex cambios basados en elfiltered formación. Esto provoca laResult componente que coincide con el índice que se marcará como seleccionado, lo que provocará una nueva representación

PROBLEMA

Inicialmente probé esto con una pequeña variedad defiles, usando la versión de desarrollo de React, y todo funcionó bien.

El problema apareció cuando tuve que lidiar con unfiles matriz de hasta 10000 entradas. Escribir 2 letras en la entrada generaría una gran lista y cuando presioné las teclas arriba y abajo para navegar, sería muy lento.

Al principio no tenía un componente definido para elResult elementos y simplemente estaba haciendo la lista sobre la marcha, en cada render de laSearch 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 puedes ver, cada vez quecurrentlySelectedIndex cambiado, causaría una nueva representación y la lista se volvería a crear cada vez. Pensé que desde que había establecido unkey valor en cadali element React evitaría volver a representar cada otroli elemento que no tenía suclassName cambio, pero aparentemente no fue así.

Terminé definiendo una clase para elResult elementos, donde verifica explícitamente si cadaResult El elemento debe volver a renderizarse en función de si se seleccionó previamente y en función de la entrada actual del usuario:

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

Y la lista ahora se crea 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));
}

Esto hizo rendimientoligeramente mejor, pero aún no es lo suficientemente bueno. La cosa es cuando probé en la versión de producción de React, las cosas funcionaron sin problemas, sin retraso alguno.

LÍNEA DE FONDO

¿Es normal una discrepancia tan notable entre las versiones de desarrollo y producción de React?

¿Estoy entendiendo / haciendo algo mal cuando pienso en cómo React gestiona la lista?

ACTUALIZACIÓN 14-11-2016

He encontrado esta presentación de Michael Jackson, donde aborda un tema muy similar a este:https://youtu.be/7S8v8jfLb1Q?t=26m2s

La solución es muy similar a la propuesta por AskarovBeknarresponderabajo

ACTUALIZACIÓN 14-4-2018

Dado que aparentemente esta es una pregunta popular y las cosas han progresado desde que se hizo la pregunta original, si bien lo aliento a que vea el video vinculado anteriormente, para obtener una idea de un diseño virtual, también le recomiendo que use elReaccionar virtualizado biblioteca si no desea reinventar la rueda.

Respuestas a la pregunta(8)

Su respuesta a la pregunta