Sono in procinto di implementare un elenco filtrabile con React. La struttura della lista è come mostrato nell'immagine qui sotto.Elenco grandi prestazioni con React
PREMESSA
Ecco una descrizione di come dovrebbe funzionare:
- Lo stato risiede nel componente più alto livello, il componente
Search
. - Lo stato è descritto come segue:
{ visible : boolean, files : array, filtered : array, query : string, currentlySelectedIndex : integer }
files
è una potenzialmente molto grandi percorsi di file, matrice contenente (10000 voci è un numero plausibile).filtered
è l'array filtrato dopo che l'utente ha digitato almeno 2 caratteri. So che si tratta di dati derivati e come tale potrebbe essere fatto per archiviarlo nello stato ma è necessario percurrentlySelectedIndex
che è l'indice dell'elemento attualmente selezionato dall'elenco filtrato.tipi più utenti 2 lettere nel componente
Input
, la matrice viene filtrato e per ogni voce nella matrice filtrato un componenteResult
è resaOgni componente
Result
sta visualizzando il percorso completo che parzialmente abbinato il query, e la parte di corrispondenza parziale del percorso è evidenziata. Per esempio il DOM di un componente Risultato, se l'utente avesse digitato 'le' sarebbe qualcosa di simile a questo:<li>this/is/a/fi<strong>le</strong>/path</li>
- Se l'utente preme i tasti su o giù mentre la componente
Input
è focalizzato lecurrentlySelectedIndex
modifiche basato sull'arrayfiltered
. In questo modo il componenteResult
che corrisponde all'indice da marcare come selezionato causando un re-rendering
PROBLEMA
Inizialmente ho provato questo con una piccola gamma sufficiente di files
, utilizzando la versione di sviluppo di React e tutto ha funzionato bene
Il problema si presentava quando avevo a che fare con un array files
grande come 10000 voci. Digitando 2 lettere nell'input si generava una grande lista e quando si premevano i tasti su e giù per spostarsi sarebbe molto lenta.
In un primo momento non ha avuto un componente definito per i Result
elementi e Stavo semplicemente facendo la lista al volo, su ogni rendering del componente Search
, come ad esempio:
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));
Come si può dire ogni volta che lo currentlySelectedIndex
cambiava, causava un nuovo rendering e l'elenco veniva ricreato ogni volta. Ho pensato che dal momento che avevo impostato un valore key
su ogni elemento li
, React evitava di ri-rendering ogni altro elemento li
che non aveva la sua modifica className
, ma a quanto pare non era così.
ho finito per la definizione di una classe per gli Result
elementi, dove controlla se esplicitamente se ogni elemento Result
dovrebbe ri-renderizzare in base al fatto che è stato precedentemente selezionato e basato sul input dell'utente corrente:
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 l'elenco è stato creato come tale:
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));
}
Ciò ha reso le prestazioni leggermente meglio, ma non è ancora abbastanza buono. Il fatto è che quando ho provato la versione di produzione di React, le cose hanno funzionato bene, senza problemi.
bottomline
è una discrepanza tale evidente tra le versioni di sviluppo e produzione di Reagire normale?
Sono in grado di capire/fare qualcosa di sbagliato quando penso a come React gestisce l'elenco?
UPDATE 14-11-2016
ho trovato questa presentazione di Michael Jackson, in cui egli affronta un problema molto simile a questo: https://youtu.be/7S8v8jfLb1Q?t=26m2s
La soluzione è molto simile a quella proposto da AskarovBeknar's answer, sotto
Che cosa si intende per sviluppo/versione di produzione di reagire? – Dibesjr
@Dibesjr https://facebook.github.io/react/downloads.html#development-vs.-production-builds –
Ah capisco, grazie. Quindi, per rispondere a una delle tue domande, dice che c'è una discrepanza nell'ottimizzazione tra le versioni.Una cosa a cui prestare attenzione nelle grandi liste è la creazione di funzioni nel rendering. Avrà un impatto sulle prestazioni quando entrerai in una lista gigante. Vorrei provare a vedere quanto tempo ci vuole per generare quell'elenco usando i loro strumenti perf https://facebook.github.io/react/docs/perf.html – Dibesjr