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 paracurrentlySelectedIndex
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>
Input
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ónPROBLEMA
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.