Reputation: 2512
When rendering a long list of elements in a table for example, then any call to setState
regardless of whether it changes the data the table uses for enumeration results in a re-render of every child object.
Every check/uncheck will re-render every element. This causes major slowdown with 2000 elements that are more complex. I will use virtualization, but want to make sure this is as performant as possible before doing so.
class App extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
const rows = Array.from({ length: 200 }, (v, k) => k + 1).map(i => ({
id: i,
field1: "hello",
field2: "cruel",
field3: "world"
}));
this.state = {
selectedIds: [],
rows
};
}
onChange(e) {
const name = e.target.name;
const checked = e.target.checked;
const selectedIds = [...this.state.selectedIds];
if (!checked) {
const index = selectedIds.findIndex(x => x === name);
selectedIds.splice(index, 1);
} else {
selectedIds.push(name);
}
this.setState(state => ({ selectedIds }));
}
render() {
const { rows, selectedIds } = this.state;
return (
<div className="App">
<h5>{selectedIds.length} Rows Selected</h5>
<table>
<thead>
<tr>
<td>Select</td>
</tr>
<tr>
<td>Field 1</td>
</tr>
<tr>
<td>Field 2</td>
</tr>
<tr>
<td>Field 3</td>
</tr>
</thead>
<tbody>
{rows.map(row => {
console.log(row);
return (
<tr key={row.id}>
<td>
<input
type="checkbox"
onChange={this.onChange}
name={row.id}
/>
</td>
<td>
<div>{row.field1}</div>
</td>
<td>
<div>{row.field2}</div>
</td>
<td>
<div>{row.field3}</div>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}
I see from other answers that it is the expected behaviour to re-run the render
function
ReactJS - Does render get called any time "setState" is called?
But if so, when the number of elements is increased to say 4000 in that link, why is there so much slowdown when clicking? Significant slowdown is noticed when only using 200 items when using slightly more complex custom React Components.
Upvotes: 2
Views: 1810
Reputation: 1442
To solve this issue you'll want to create an additional component for the row item. This way it will have a separate render function and not rerender itself because the row component hasn't changed, only the table component.
{rows.map(row => {
return (
<RowItem row={row} onChange={this.onChange}>
);
}}
and your new component will look something like this:
class RowItem extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<tr key={this.props.row.id}>
<td>
<input
type="checkbox"
onChange={this.props.onChange}
name={this.props.row.id}
/>
</td>
<td>
<div>{this.props.row.field1}</div>
</td>
<td>
<div>{this.props.row.field2}</div>
</td>
<td>
<div>{this.props.row.field3}</div>
</td>
</tr>
);
}
}
Upvotes: 1