title: Big list performance with React
body: <p>I am in the process of implementing a filterable list with React. The structure of the list is as shown in the image below.</p>
  
  <p><a href="https://i.stack.imgur.com/lTcZm.png" rel="noreferrer"><img src="https://i.stack.imgur.com/lTcZm.png" alt="enter image description here"></a></p>
  
  <p><strong>PREMISE</strong></p>
  
  <p>Here's a description of how it is supposed to work:</p>
  
  <ul>
  <li>The state resides in the highest level component, the <code>Search</code> component.</li>
  <li>The state is described as follows:</li>
  </ul>
  
  <pre>
  {
      visible : boolean,
      files : array,
      filtered : array,
      query : string,
      currentlySelectedIndex : integer
  }
  </pre>
  
  <ul>
  <li><code>files</code> is a potentially very large, array containing file paths (10000 entries is a plausible number).</li>
  <li><code>filtered</code> is the filtered array after the user types at least 2 characters. I know it's derivative data and as such an argument could be made about storing it in the state but it is needed for</li>
  <li><p><code>currentlySelectedIndex</code> which is the index of the currently selected element from the filtered list.</p></li>
  <li><p>User types more than 2 letters into the <code>Input</code> component, the array is filtered and for each entry in the filtered array a <code>Result</code> component is rendered</p></li>
  <li><p>Each <code>Result</code> component is displaying the full path that partially matched the query, and the partial match part of the path is highlighted. For example the DOM of a Result component, if the user had typed 'le' would be something like this :</p>
  
  <p><code>&lt;li&gt;this/is/a/fi&lt;strong&gt;le&lt;/strong&gt;/path&lt;/li&gt;</code></p></li>
  <li>If the user presses the up or down keys while the <code>Input</code> component is focused the <code>currentlySelectedIndex</code> changes based on the <code>filtered</code> array. This causes the <code>Result</code> component that matches the index to be marked as selected causing a re-render</li>
  </ul>
  
  <p><strong>PROBLEM</strong></p>
  
  <p>Initially I tested this with a small enough array of <code>files</code>, using the development version of React, and all worked fine. </p>
  
  <p>The problem appeared when I had to deal with a <code>files</code> array as big as 10000 entries. Typing 2 letters in the Input would generate a big list and when I pressed the up and down keys to navigate it it would be very laggy.</p>
  
  <p>At first I did not have a defined component for the <code>Result</code> elements and I was merely making the list on the fly, on each render of the <code>Search</code> component, as such:</p>
  
  <pre><code>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 (
           &lt;li onClick={this.handleListClick}
               data-path={file}
               className={(index === this.state.currentlySelected) ? "valid selected" : "valid"}
               key={file} &gt;
               {start}
               &lt;span className="marked"&gt;{match}&lt;/span&gt;
               {end}
           &lt;/li&gt;
       );
  }.bind(this));
  </code></pre>
  
  <p>As you can tell, every time the <code>currentlySelectedIndex</code> changed, it would cause a re-render and the list would be re-created each time. I thought that since I had set a <code>key</code> value on each <code>li</code> element React would avoid re-rendering every other <code>li</code> element that did not have its <code>className</code> change, but apparently it wasn't so.</p>
  
  <p>I ended up defining a class for the <code>Result</code> elements, where it explicitly checks whether each <code>Result</code> element should re-render based on whether it was previously selected and based on the current user input :</p>
  
  <pre><code>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 (
              &lt;li onClick={this.props.handleListClick}
                  data-path={this.props.file}
                  className={
                      (this.props.selected) ? "valid selected" : "valid"
                  }
                  key={this.props.file} &gt;
                  {this.props.children}
              &lt;/li&gt;
          );
      }
  });
  </code></pre>
  
  <p>And the list is now created as such: </p>
  
  <pre><code>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 (
          &lt;ResultItem handleClick={this.handleListClick}
              data-path={file}
              selected={selected}
              key={file}
              match={match} &gt;
              {start}
              &lt;span className="marked"&gt;{match}&lt;/span&gt;
              {end}
          &lt;/ResultItem&gt;
      );
  }.bind(this));
  }
  </code></pre>
  
  <p>This made performance <em>slightly</em> better, but it's still not good enough. Thing is when I tested on the production version of React things worked buttery smooth, no lag at all. </p>
  
  <p><strong>BOTTOMLINE</strong></p>
  
  <p><strong>Is such a noticeable discrepancy between development and production versions of React normal?</strong></p>
  
  <p><strong>Am I understanding/doing something wrong when I think about how React manages the list?</strong> </p>
  
  <p><strong>UPDATE 14-11-2016</strong></p>
  
  <p>I have found this presentation of Michael Jackson, where he tackles an issue very similar to this one: <a href="https://youtu.be/7S8v8jfLb1Q?t=26m2s" rel="noreferrer">https://youtu.be/7S8v8jfLb1Q?t=26m2s</a></p>
  
  <p>The solution is very similar to the one proposed by AskarovBeknar's <a href="https://stackoverflow.com/a/38193164/4651083">answer</a>, below</p>
  
  <p><strong>UPDATE 14-4-2018</strong></p>
  
  <p>Since this is apparently a popular question and things have progressed since the original question was asked, while I do encourage you to watch the video linked above, in order to get a grasp of a virtual layout, I also encourage you to use the <a href="https://github.com/bvaughn/react-virtualized" rel="noreferrer">React Virtualized</a> library if you do not want to re-invent the wheel. </p>