2016-04-19 14 views
4

Ich lerne ReactJS und versuche, eine Anwendung darauf zu bauen.Nicht blockierende Darstellung in ReactJS

Wenn ich versuche, meinen Status zu ändern und zu rendern, friert meine Seite ein und kann nichts tun, bis das Rendern beendet ist, wenn meine Komponenten riesig werden.

Ich habe festgestellt, dass ich shouldComponentUpdate verwenden kann, um meinen Code zu optimieren, aber die Frage kommt zu mir ist: Kann ich diese Render-Prozedur nicht blockieren? Und so kann ich dem Benutzer sagen, dass die Seite einige schwere Ladevorgänge ausführt und bitte warten oder den Fortschritt der Ausführung anzeigen? Oder wenn der Benutzer das Rendern abbrechen kann, zum Beispiel für einen Live-Editor, wenn der Benutzer den Inhalt des Editors bearbeitet, hört der Abschnitt "Vorschau" auf, alte Inhalte zu rendern und neuen Inhalt zu rendern, ohne die Benutzeroberfläche des Editors zu blockieren.

Hier ist die starke Belastung Beispielcode:

<!DOCTYPE html> 
 
<html> 
 
    <head> 
 
    <meta charset="utf-8" /> 
 
    <title>React Tutorial</title> 
 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react.js"></script> 
 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react-dom.js"></script> 
 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.js"></script> 
 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script> 
 
    </head> 
 
    <body> 
 
    <div id="content"></div> 
 
    <script type="text/babel"> 
 
     var Box = React.createClass({ 
 
     render: function() { 
 
      return (
 
      <div>Box</div> 
 
     ); 
 
     } 
 
     }); 
 
     
 
     var CommentBox = React.createClass({ 
 
     getInitialState: function() { 
 
      return {box_count: 5}; 
 
     }, 
 
     
 
     heavyLoadRender: function() { 
 
      this.setState({box_count: 40000}); 
 
     }, 
 
     
 
     render: function() { 
 
      var render_box = []; 
 
      for (var i=0; i<this.state.box_count; i++) { 
 
      render_box.push(<Box />); 
 
      } 
 
      return (
 
      <div> 
 
       {render_box} 
 
       <button onClick={this.heavyLoadRender}>Start</button> 
 
      </div> 
 
     ); 
 
     } 
 
     }); 
 
     
 
     ReactDOM.render(
 
     <CommentBox />, 
 
     document.getElementById('content') 
 
    ); 
 
    </script> 
 
    </body> 
 
</html>

Wenn ich Start drücken, friert die Seite und keine Antwort, bis alle Box gemacht wird. Ist es möglich, eine Schaltfläche mit dem Namen Cancel hinzuzufügen, welcher Benutzer das Rendern abbrechen und alle Boxen löschen kann?

Antwort

4

Dies ist eine gute Frage und ein perfekter Anwendungsfall für setTimeout, die ein Update für die nächste Runde der Ereignisschleife planen können.

Anstatt die Anzahl der zu rendernden Komponenten zu speichern, speichern Sie ein Array von Komponenten und rendern Sie diese direkt. jsfiddle

var CommentBox = React.createClass({ 
    getInitialState: function() { 
     return { boxes: [<Box key="first" />] }; 
    }, 

    heavyLoadRender: function() { 
     setTimeout(() => { 
     if (this.state.boxes.length < 50000) { 
      this.setState({ 
      boxes: this.state.boxes.concat(<Box key={this.state.boxes.length} />) 
      }) 
      this.heavyLoadRender() 
     } 
     }) 
    }, 

    render: function() { 
     return (
     <div> 
      <button onClick={this.heavyLoadRender}>Start</button> 
      {this.state.boxes} 
     </div> 
    ) 
    } 
    }) 

Update:

Wenn Sie nur den Zustand zeigen wollen, sobald das Array gefüllt ist, nichts an, bis es die Größe hits:

Dies tat funktioniert nicht:

{ this.state.boxes.length === 50000 && this.state.boxes } 

Hoffnung ist nicht verloren, obwohl! Benutze Stil!

<div style={{ display: this.state.boxes.length === 50000 ? 'block' : 'none' }}> 
    { this.state.boxes } 
</div> 

Wenn Sie die Geschwindigkeit erhöhen möchten, können Sie mehr als Element schieben pro setTimeout

var newBoxes = [] 
for (var i = 0; i < 5; i++) { 
    newBoxes.push(<Box />) 
} 
this.setState({ 
    boxes: this.state.boxes.concat(newBoxes) 
}) 

updated fiddle. Ich denke, dass diese ganze Klasse von Problemen einige Zeit brauchen wird. In Chargen von 10.000 blockiert die Basis-Box-Komponente nicht und Sie könnten leicht einen Ladespinner nach oben werfen.

+0

Danke für die Antwort. Diese Antwort funktioniert gut (nicht die Benutzeroberfläche zu blockieren) mit dem von mir bereitgestellten Beispiel, aber es gibt mehrere Probleme. Erstens dauert diese Methode zu lange, um sie mit der ursprünglichen Methode zu vergleichen. Zweitens, wenn die Box eine große Komponente ist, zum Beispiel, wenn es eine 'BigBox' ist, die 200' Box' enthält, wird die Benutzeroberfläche durch diese Methode immer noch verzögert. Schließlich erzeugt diese Methode in jedem Zustand Boxen, die dem Benutzer das Zwischenergebnis anzeigen. Was ist, wenn ich möchte, dass der Benutzer das Endergebnis statt des vorläufigen Ergebnisses sieht? – Nier

+0

Ihr letzter Punkt ist einfach, {this.state.boxes.length === max && this.state.boxes} '..und wenn Ihre 'Box'-Komponente lange braucht, um zu rendern, müssen Sie möglicherweise auch ihre Boxen auf nicht-blockierende Weise füllen. – azium

+0

Und Sie können die Renderrate durch das Batching von concats beschleunigen. Aktualisierte Antwort – azium

Verwandte Themen