Große Listenleistung mit React

Ich bin dabei, mit React eine filterbare Liste zu implementieren. Die Struktur der Liste ist in der folgenden Abbildung dargestellt.

PRÄMISS

Hier ist eine Beschreibung, wie es funktionieren soll:

Der Status befindet sich in der Komponente der höchsten Ebene, demSearch component. Der Zustand wird wie folgt beschrieben:
{
    visible : boolean,
    files : array,
    filtered : array,
    query : string,
    currentlySelectedIndex : integer
}
files ist ein möglicherweise sehr großes Array mit Dateipfaden (10000 Einträge sind eine plausible Zahl).filtered ist das gefilterte Array, nachdem der Benutzer mindestens 2 Zeichen eingegeben hat. Ich weiß, dass es sich um abgeleitete Daten handelt, und als solches könnte man argumentieren, sie in dem Zustand zu speichern, aber sie werden für @ benötig

currentlySelectedIndex Dies ist der Index des aktuell ausgewählten Elements aus der gefilterten Liste.

Benutzer tippt mehr als 2 Buchstaben in dasInput -Komponente wird das Array gefiltert und für jeden Eintrag im gefilterten Array einResult Komponente wird gerendert

JederResultie @ -Komponente zeigt den vollständigen Pfad an, der teilweise mit der Abfrage übereinstimmt, und der teilweise übereinstimmende Teil des Pfads wird hervorgehoben. Zum Beispiel wäre das DOM einer Result-Komponente, wenn der Benutzer 'le' eingegeben hätte, ungefähr so:

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

Wenn dies der Fall ist, drückt der Benutzer die Aufwärts- oder Abwärtstaste, während dasInput -Komponente ist fokussiert diecurrentlySelectedIndex Änderungen basierend auf demfiltered array. Dies bewirkt, dass dasResult -Komponente, die mit dem Index übereinstimmt, der als ausgewählt markiert werden soll, wodurch ein erneutes Rendern ausgelöst wird

PROBLE

Initial habe ich dies mit einem ausreichend kleinen Array von @ getestfiles, mit der Entwicklungsversion von React, und alles hat gut funktioniert.

Das Problem trat auf, als ich mich mit einem @ auseinandersetzen mussfiles Array so groß wie 10000 Einträge. Wenn Sie 2 Buchstaben in die Eingabe eingeben, wird eine große Liste erstellt, und wenn ich zum Navigieren die Auf- und Ab-Tasten drücke, wird dies sehr verzögert.

Zunächst hatte ich keine definierte Komponente für dasResult elements und ich haben die Liste nur im Handumdrehen erstellt, bei jedem Rendern desSearch -Komponente als solche:

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

Wie Sie sehen können, jedes Mal, wenn dascurrentlySelectedIndex geändert, würde es ein erneutes Rendern verursachen und die Liste würde jedes Mal neu erstellt. Ich dachte, da ich ein @ gesetzt hatkey Wert auf jedemli element React würde es vermeiden, jedes andere @ erneut zu renderli Element, das sein @ nicht hatclassName ändern, aber anscheinend war es nicht so.

m Ende definierte ich eine Klasse für dasResult -Elemente, bei denen explizit geprüft wird, ob jedesResult -Element sollte basierend darauf, ob es zuvor ausgewählt wurde und basierend auf der aktuellen Benutzereingabe neu gerendert werden:

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

Und die Liste wird nun als solche erstellt:

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

Diese Leistung gemachtleich besser, aber es ist immer noch nicht gut genug. Als ich die Serienversion von React ausprobierte, lief alles reibungslos und ohne Verzögerung.

ENDEFFEK

Ist eine solche auffällige Diskrepanz zwischen Entwicklungs- und Produktionsversion von React normal?

Verstehe / tue ich etwas falsch, wenn ich darüber nachdenke, wie React die Liste verwaltet?

UPDATE 14-11-2016

Ich habe diese Präsentation von Michael Jackson gefunden, in der er sich mit einem ähnlichen Thema befasst:https: //youtu.be/7S8v8jfLb1Q? t = 26m2s

Die Lösung ist der von AskarovBeknar vorgeschlagenen sehr ähnlichAntworte, unter

UPDATE 14-4-2018

Da dies anscheinend eine beliebte Frage ist und sich die Dinge seit der ursprünglichen Frage weiterentwickelt haben, empfehle ich Ihnen, das oben verlinkte Video anzuschauen, um ein virtuelles Layout zu erhalten. Ich empfehle Ihnen auch, das @ zu verwendeReact Virtualized Bibliothek, wenn Sie das Rad nicht neu erfinden möchten.

Antworten auf die Frage(16)

Ihre Antwort auf die Frage