github
React-virtualized is a reactive component that efficiently renders a large table and an virtualized table
Typical Development problems
The student components are:
function Student({student}) {
return <div>{student.name}</div>
}
Copy the code
If we render the entire list directly, the student list alone will generate 1000+ div tags.
Often, our student components will be:
functionStudent({student, ... rest}) {return( <div> ... <div>{student.name} .... </div> ... </div> ) }Copy the code
At this point, the number of DOM’s becomes unimaginable.
As we all know, if the DOM structure is too large, the user experience will be problematic, such as scrolling, clicking and other common operations. Meanwhile, the react virtual DOM calculation and the reflection of the virtual DOM to the real DOM will also be under great pressure. When the user clicks to switch classrooms, there will be a second lag.
Use react-Virtualized optimization
And then in the React ecology, the React-Virtualized existence as an optimization has been going on for a long time, and the community is constantly being maintained and discussed, which means that it is an ongoing issue! 😂
The core idea to solve the above problem is to load only components in the visible region
React-virtualized differentiates our scrolling scene into an internal scrolling in the viewport and an virtualized scrolling based on the viewport, which is an independent scrolling area on the page. This is internal scrolling, similar to iscroll’s scrolling, which makes scrolling part of the window’s scrolling (which is more common on mobile). Based on this, the components that currently need to be displayed are calculated.
The specific implementation
The student component is modified to:
functionStudent({student, style, ... rest}) {return( <div style={style}> ... <div>{student.name} .... </div> ... </div> ) }Copy the code
Student List component:
import React from 'react'
import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer'
import { List as VList } from 'react-virtualized/dist/commonjs/List'
class StudentList extends React.Component {
constructor(props) {
super(props)
this.state = {
list: []
}
}
getList = () => {
api.getList.then(res => {
this.setState({
list: res
})
})
}
componentDidMount() {
this.getList()
}
render() {
const { list } = this.state
const renderItem = ({ index, key, style }) => {
return <Student key={key} student={list[index]} style{style} />
}
return (
<div style={{height: 1000}}>
<AutoSizer>
{({ width, height }) => (
<VList
width={width}
height={height}
overscanRowCount={10}
rowCount={list.length}
rowHeight={100}
rowRenderer={renderItem}
/>
)}
</AutoSizer>
</div>
)
}
}
Copy the code
(The height of the outer div is not required. If you have a Flex layout, you can use Flex: 1 to calculate the height and then it is an virtualized motion.)
At this point, if each Student is the same height, the problem is basically solved!
Then again, the problem is that sometimes our students are uncertain of the height and there are two ways to solve this problem. The React-Virtualized CellMeasurer component solution is recommended
Methods a
The student list component is changed to:
import React from 'react'
import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer'
import { List as VList } from 'react-virtualized/dist/commonjs/List'
import { CellMeasurerCache, CellMeasurer } from 'react-virtualized/dist/commonjs/CellMeasurer'
class StudentList extends React.Component {
constructor(props) {
super(props)
this.state = {
list: []
}
}
measureCache = new CellMeasurerCache({
fixedWidth: true,
minHeight: 58
})
getList = () => {
api.getList.then(res => {
this.setState({
list: res
})
})
}
componentDidMount() {
this.getList()
}
render() {
const { list } = this.state
const renderItem = ({ index, key, parent, style }) => {
return( <CellMeasurer cache={this.measureCache} columnIndex={0} key={key} parent={parent} rowIndex={index}> <Student key={key} student={list[index]} /> </CellMeasurer> ) }return (
<div style={{height: 1000}}>
<AutoSizer>
{({ width, height }) => (
<VList
ref={ref => this.VList = ref}
width={width}
height={height}
overscanRowCount={10}
rowCount={list.length}
rowHeight={this.getRowHeight}
rowRenderer={renderItem}
deferredMeasurementCache={this.measureCache}
rowHeight={this.measureCache.rowHeight}
/>
)}
</AutoSizer>
</div>
)
}
}
Copy the code
Method 2
Use the react-height or react-height method mentioned in the issue to calculate callbacks. Use react-height as an example:
The student list component is changed to:
import React from 'react'
import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer'
import { List as VList } from 'react-virtualized/dist/commonjs/List'
import ReactHeight from 'react-height'
class StudentList extends React.Component {
constructor(props) {
super(props)
this.state = {
list: []
heights = []
}
}
getList = () => {
api.getList.then(res => {
this.setState({
list: res
})
})
}
componentDidMount() {
this.getList()
}
handleHeightReady = (height, index) => {
const heights = [...this.state.heights]
heights.push({
index,
height
})
this.setState({
heights
}, this.vList.recomputeRowHeights(index))
}
getRowHeight = ({ index }) => {
const row = this.heights.find(item => item.index === index)
return row ? row.height : 100
}
render() {
const { list } = this.state
const renderItem = ({ index, key, style }) => {
if (this.heights.find(item => item.index === index)) {
return <Student key={key} student={list[index]} style{style} />
}
return (
<div key={key} style={style}>
<ReactHeight
onHeightReady={height => {
this.handleHeightReady(height, index)
}}
>
<Student key={key} student={list[index]} />
</ReactHeight>
</div>
)
}
return (
<div style={{height: 1000}}>
<AutoSizer>
{({ width, height }) => (
<VList
ref={ref => this.VList = ref}
width={width}
height={height}
overscanRowCount={10}
rowCount={list.length}
rowHeight={this.getRowHeight}
rowRenderer={renderItem}
/>
)}
</AutoSizer>
</div>
)
}
}
Copy the code
Now, if all your tabular data is obtained at once, you’re almost done!
What about scroll loading?
React-virtualized officially has an InfiniteLoader!
If put aside this classic case, the development is a chat box?
Chat box is the first time, according to a reverse order is loaded into the data, the position of the scroll bar should be located at the bottom, the react – the List of virtualized component exposes scrollToRow (index) method to implement to us, Student highly inconsistent with direct use of have a little problem, You can’t scroll to the bottom all at once. A temporary solution is:
scrollToRow = (): void => {
const rowIndex = this.props.list.length - 1
this.vList.scrollToRow(rowIndex)
clearTimeout(this.scrollToRowTimer)
this.scrollToRowTimer = setTimeout(() => {
if (this.vList) {
this.vList.scrollToRow(rowIndex)
}
}, 10)
}
Copy the code
Called when the data is first loaded
InfiniteLoader does not support the need for reverse loading, so you can only use the onScroll method to get the data and perform related operations. Note that if you use method 1 to return the data from the previous page, Need to do this. MeasureCache. The clear/clearAll, notify the react – virtualized computing again. The second method should add all the indexes in the state.heights array to the number of the data
getList = () => {
api.getList.then(res => {
const heights = [...this.state.heights]
heights.map(item => {
return {
index: item.index + res.length,
height: item.height
}
})
this.setState({
list: [...res, ...this.state.list],
heights
})
})
}
Copy the code
And then the React-Virtualized mode has a lot of interesting features and the realization of the virtualized mode is also very valuable! And then you can go to React-Virtualized Github