Je suis dans le processus de mise en œuvre d'une liste filtrable à Réagir. La structure de la liste, comme illustré dans l'image ci-dessous.
PRÉMISSE
Voici une description de la façon dont il est censé fonctionner:
- L'état réside dans le niveau le plus élevé de la composante, l'
Search
de la composante. - L'état est décrit comme suit:
{ visible : boolean, fichiers : array, filtré : array, requête : string, currentlySelectedIndex : integer }
-
files
est un très grand, un tableau contenant les chemins de fichiers (10000 entrées est plausible). -
filtered
est filtré tableau une fois que l'utilisateur tape au moins 2 caractères. Je sais que c'est dérivé de données et qu'un tel argument pourrait être faite à propos de les stocker dans l'état mais il est nécessaire pour currentlySelectedIndex
qui est l'index de l'élément sélectionné de la liste filtrée.Types d'utilisateurs de plus de 2 lettres dans l'
Input
de la composante, le tableau est filtré et pour chaque entrée dans le tableau filtré unResult
composant est rendu-
Chaque
Result
composant affiche le chemin d'accès complet que partiellement correspondait à la requête, et la correspondance partielle une partie du chemin est en surbrillance. Par exemple, le DOM d'une composante de Résultat, si l'utilisateur a tapé "chier" serait quelque chose comme ceci :<li>this/is/a/fi<strong>le</strong>/path</li>
- Si l'utilisateur appuie sur les touches up ou down, tandis que l'
Input
vise l'currentlySelectedIndex
des modifications basées sur l'filtered
tableau. Cela provoque l'Result
de la composante qui correspond à l'index pour être sélectionné provoquant un nouveau rendu
PROBLÈME
J'ai d'abord testé avec un assez petit tableau d' files
, à l'aide de la version de développement de Réagir, et tout a bien fonctionné.
Le problème est apparu lorsque j'ai eu à traiter avec un files
tableau aussi grand que 10 000 entrées. En tapant 2 lettres dans l'Entrée serait de générer une grande liste de et quand j'ai appuyé sur les touches haut et bas pour naviguer, se serait très lag.
Au début, je n'ai pas la définition d'une composante de l' Result
- éléments et j'ai été contente de faire la liste à la volée, sur chaque rendu de l' Search
composant, comme tel:
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));
Comme vous pouvez le dire, chaque fois que l' currentlySelectedIndex
changé, il serait la cause d'une ré-rendu et la liste serait re-créé à chaque fois. J'ai pensé que, puisque j'avais mis un key
de la valeur de chaque li
élément Réagir permettrait d'éviter de re-rendu de tous les autres li
élément qui n'a pas son className
changer, mais apparemment ce n'était pas le cas.
J'ai fini par la définition d'une classe pour l' Result
éléments, où il est explicitement vérifie si chaque Result
élément de re-rendre basée sur le fait qu'il a été précédemment sélectionné et basée sur la saisie de l'utilisateur :
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>
);
}
});
Et la liste est maintenant créé comme tel:
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));
}
De ce fait la performance légèrement meilleure, mais c'est pas encore suffisant. C'est quand je l'ai testé sur la version de production de Réagir choses travaillé de beurre lisse, pas de lag du tout.
La ligne de FOND
Est un tel écart net entre le développement et la production de versions de Réagir normal?
Suis-je comprendre/faire quelque chose de mal quand je pense à comment Réagir gère la liste?
Mise à JOUR 14-11-2016
J'ai trouvé cette présentation de Michael Jackson, où il aborde une question très similaire à ceci: https://youtu.be/7S8v8jfLb1Q?t=26m2s
La solution est très similaire à celui proposé par AskarovBeknar de réponse, ci-dessous
Mise à JOUR 14-4-2018
Puisque c'est apparemment une question populaire et les choses ont avancé depuis que la question initiale a été posée, alors que je ne vous encourage à regarder la vidéo ci-dessus, afin d'obtenir une prise de un virtuel, mise en page, je vous encourage à utiliser le Réagir Virtualisé bibliothèque si vous ne voulez pas ré-inventer la roue.