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 más alto nivel, el
Searchcomponente. - El estado se describe de la siguiente manera:
{
visible: booleano,
archivos: matriz,
filtrado: matriz,
consulta: cadena,
currentSelectedIndex: integer
}
fileses una matriz potencialmente muy grande que contiene rutas de archivo (10000 entradas es un número plausible).filteredes 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 almacenarlos en el estado, pero es necesario paracurrentlySelectedIndexque es el índice del elemento seleccionado actualmente de la lista filtrada.El usuario escribe más de 2 letras en el
Inputcomponente, la matriz se filtra y por cada entrada en la matriz filtradaResultse representa un componenteCada
Resultcomponente muestra la ruta completa que coincidió parcialmente con la consulta, y la parte de coincidencia parcial de la ruta está resaltada. Por ejemplo, el DOM de un componente Result, 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 mientras el
Inputcomponente está enfocado, loscurrentlySelectedIndexcambios se basan en lafilteredmatriz. Esto hace que elResultcomponente que coincide con el índice se marque como seleccionado provocando una re-renderización
PROBLEMA
Inicialmente probé esto con una matriz lo suficientemente pequeña files, usando la versión de desarrollo de React, y todo funcionó bien.
El problema apareció cuando tuve que lidiar con una filesmatriz de hasta 10000 entradas. Escribir 2 letras en la entrada generaría una gran lista y cuando presioné las teclas de arriba y abajo para navegar sería muy lento.
Al principio, no tenía un componente definido para los Resultelementos y simplemente estaba haciendo la lista sobre la marcha, en cada renderizado del Searchcomponente, 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 puede ver, cada vez que se currentlySelectedIndexcambia, se vuelve a generar y la lista se vuelve a crear cada vez. Pensé que dado que había establecido un keyvalor en cada lielemento, React evitaría volver a renderizar todos los demás lielementos que no tuvieran su classNamecambio, pero aparentemente no fue así.
Terminé definiendo una clase para los Resultelementos, donde verifica explícitamente si cada Resultelemento 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 que el rendimiento fuera un poco mejor, pero aún no es lo suficientemente bueno. La cosa es que cuando probé en la versión de producción de React, las cosas funcionaron perfectamente, sin ningún retraso.
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 la respuesta de AskarovBeknar , a continuación
ACTUALIZACIÓN 14-4-2018
Dado que esta es aparentemente una pregunta popular y las cosas han progresado desde que se hizo la pregunta original, aunque te animo a que veas el video vinculado anteriormente, para que comprendas un diseño virtual, también te animo a usar React Virtualized biblioteca si no quieres reinventar la rueda.



