How to Improve Material-UI List Performance
The Problem
If you've ever used Material UI components for a React project, you've most likely ran into performance issues with displaying large lists. I ran into this issue several times where whenever I had a list over a certain length it made the list very unresponsive to user interaction, even in Chrome.
Check out this example on Stack Blitz: https://react-smo644.stackblitz.io
Try to open the list and select a number and you'll see the poor performance. Now I did put 2000 items in this list so to be fair this may be an extreme case.
The whole example looks just like this:
import React, { Component } from 'react';
import { render } from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import './style.css';
const options = [];
for (var i = 1; i <= 2000; i++) options.push(i);
class App extends Component {
state = {
value: null
};
handleChange = (event, index, value) => this.setState({value});
render() {
return (
<MuiThemeProvider>
<div>
<SelectField
floatingLabelText="Count"
value={this.state.value}
onChange={this.handleChange}
>
{options.map(o => <MenuItem key={o} value={o} primaryText={o} />)}
</SelectField>
</div>
</MuiThemeProvider>
);
}
}
render(<App />, document.getElementById('root'));
The Solution
There's an npm package that is excellent at solving this problem. Check it out: react-virtualized
Here's our above example updated to use react-virtualized for the list:
import React, { Component } from 'react';
import { render } from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import { List } from 'react-virtualized';
import './style.css';
const options = [];
for (var i = 1; i <= 2000; i++) options.push(i);
class App extends Component {
state = {
value: null
};
handleChange = value => this.setState({value});
rowRenderer = ({
key,
index,
isScrolling,
isVisible,
style
}) => {
return (
<MenuItem
key={key}
style={style}
value={options[index]}
primaryText={options[index]}
onClick={() => this.handleChange(options[index])}
/>
)
}
render() {
return (
<MuiThemeProvider>
<div>
<SelectField
floatingLabelText="Count"
value={this.state.value}
onChange={this.handleChange}
>
<List
width={300}
height={300}
rowCount={options.length}
rowHeight={40}
rowRenderer={this.rowRenderer}
/>
</SelectField>
</div>
</MuiThemeProvider>
);
}
}
render(<App />, document.getElementById('root'));
Check out a live example on stackblitz: https://react-wxgkzm.stackblitz.io/.
You'll notice this list is much easier to interact with. It pulls up faster, scrolls faster and closes faster. This is due to react-virtualized taking care of only rendering a subsection of the list so that only those that are on display or near those on display are in the DOM.