18

I'm trying to create a 8-column table, each column contains <input /> element. For some reason, I experience a delay in the on change event of the text input. Reducing the number of columns to below 4 makes the experience better. It could make some sense, but I also tried to increase the amount of columns, and I found out that for 10 or more columns, experience is excellent again. You can check my super simple React app, in which you can dynamically change the amount of columns - http://com.react.table-with-inputs.s3-website.eu-central-1.amazonaws.com/.

And this is my code:

import React from 'react';
import { AutoSizer, Table, Column } from 'react-virtualized';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      numOfCols: '8',
    };
  }

  rowGetter = ({ index }) => {
    return { index };
  };

  renderHeaderCell = ({ label }) => {
    return (
      <span>{label}</span>
    );
  };

  renderCell = ({ rowIndex, columnIndex }) => {
    return (
      <input key={`input-${rowIndex}-${columnIndex}`} />
    );
  };

  render() {
    return (
      <div style={{ display: 'flex', flex: 1, flexDirection: 'column', padding: '10px' }}>
        <span>NUMBER OF COLUMNS</span>
        <input
          value={this.state.numOfCols}
          onChange={e => this.setState({ numOfCols: e.target.value })}
        />
        <br />
        <br />
        <AutoSizer style={{ flex: 1 }}>
          {({ height, width }) => (
            <Table
              width={width}
              height={height}
              estimatedRowSize={36}
              overscanRowCount={10}
              headerHeight={30}
              rowHeight={36}
              rowCount={20}
              rowGetter={this.rowGetter}
            >
              {
                (() => {
                  const cols = [];

                  for (let i = 0; i < parseInt(this.state.numOfCols); i += 1) {
                    cols.push(
                      <Column
                        label={`Title${i + 1}`}
                        dataKey={`title${i + 1}`}
                        width={120}
                        cellRenderer={this.renderCell}
                        headerRenderer={this.renderHeaderCell}
                      />
                    )
                  }

                  return cols;
                })()
              }
            </Table>
          )}
        </AutoSizer>
      </div>
    );
  }
}

export default App;

Edit 1: This happens on Chrome only (Mac & Windows, desktop & mobile). It does not reproduce on Safari or Firefox (not desktop version and not mobile version).

Edit 2: I tried to remove the AutoSizer but nothing changed. Uploaded an updated version with the ability to render or not the Table with AutoSizer container (http://com.react.table-with-inputs.s3-website.eu-central-1.amazonaws.com/).

Asaf
  • 2,158
  • 2
  • 25
  • 40
  • For me even 20 columns work fine? – Tarun Lalwani Aug 04 '19 at 14:22
  • I just tried your link and quickly punched in all the numbers from 1 to 25. I didn't experience any delay in rendering. There must be something else about the environment that is causing the issue. – Michael Cheng Aug 04 '19 at 15:03
  • @TarunLalwani 20 cols works fine for me too, but with 5 to 9 cols I get delayed on change in the inputs elements. – Asaf Aug 04 '19 at 16:24
  • @MichaelCheng I tried it on multiple OS, Browsers and so on. Have you compared the typing in one input (when you have 8 cols) to a "regular" input on a different website (such as comment box in SO :) ? – Asaf Aug 04 '19 at 16:25
  • Works fine with any number of columns for me. Can you run a development build and see what all events are happening? – Tarun Lalwani Aug 04 '19 at 16:38
  • @asaf Yes, tried that too. I’m on a phone now and I don’t experience any delay typing in any of the inputs I tried regardless of column sizes. You have two users stating that it cannot be reproduced. Without more environment information, this doesn’t seem like something that is going to be easily reproduced. – Michael Cheng Aug 04 '19 at 17:33
  • @MichaelCheng check my latest editing. Do you experience it on Chrome (desktop or mobile)? – Asaf Aug 11 '19 at 06:07
  • I tried it under Win 10, Chrome from PortableApps, and saw no delays for either 8 or 12 columns. Would you give it a try? A few (cumbersome) things you could try to isolate the issue are: 1) Testing on different machines, 2) Testing in a Virtual Machine in a system showing the issue. – sancho.s ReinstateMonicaCellio Aug 11 '19 at 08:29

1 Answers1

0

I havnt reproduced the issue, however I've seen multiple points in your code that can create performance problems in that code so here is my try.

First thing first, have you tryed reducing rendering count?

For example maybe your component can be switched to PureComponent so that it rerender only on state and props change. This can improve performance. You'll need extensive testing as you need to check if subcomponent need rerendering or not.

Remove the inline style as this creates a new object everytime, thus destroying every rendering optimisation react can make. If you still need style={something} then move something to a const variable outside of class declaration to ensure the object is created one time. Please not that this is a bad practice so check every other occurrence in your code.

Right there I see at least two places where react will rerender an element but that element havn't changed as objects content are hardcoded.

Why are you creating a new function then execute it inline like that?

A more efficient way to do that would be to use lodash's map:

map(range(this.state.numOfCols), i => (<Column
  label={`Title${i + 1}`}
  dataKey={`title${i + 1}`}
  width={120}
  cellRenderer={this.renderCell}
  headerRenderer={this.renderHeaderCell}
/>))

Once all the obvious issues are fixed, install a perf package such as https://github.com/welldone-software/why-did-you-render and check why your App component render too much.

Maybe some code not included in your example is causing the component to rerender every time?

Edit: after rereading the code, I'm 75% sure the problem is with style={{ flex: 1 }} causing a full AutoSizer rerender !

Atrakeur
  • 4,126
  • 3
  • 17
  • 22
  • Moving style={{ flex: 1 }} to a static variable did not change a thing. Also, as I understand and accept (most of) your comments, unfortunately, it does not affect the input issue. – Asaf Aug 11 '19 at 06:00