Skip to content

Instantly share code, notes, and snippets.

@bvaughn
Created August 27, 2016 15:38
Show Gist options
  • Save bvaughn/9c7af838646942106a2ff689bbc7a1ff to your computer and use it in GitHub Desktop.
Save bvaughn/9c7af838646942106a2ff689bbc7a1ff to your computer and use it in GitHub Desktop.
/** @flow */
import React, { Component } from 'react'
import { ContentBox, ContentBoxHeader, ContentBoxParagraph } from '../demo/ContentBox'
import Immutable from 'immutable'
import InfiniteLoader from './InfiniteLoader'
import FlexTable from '../FlexTable/FlexTable'
import FlexColumn from '../FlexTable/FlexColumn'
function noop () {}
const ROWS_TO_FETCH_AT_A_TIME = 10
export default class InfiniteLoaderExample extends Component {
constructor (props) {
super(props)
this.state = {
listSize: 0,
loadedRowCount: 0,
numLoadingRows: 0
}
this._loadedRowsMap = {}
this._timeoutIdMap = {}
this._isRowLoaded = this._isRowLoaded.bind(this)
this._loadMoreRows = this._loadMoreRows.bind(this)
this._rowGetter = this._rowGetter.bind(this)
this._setNewLimit = this._setNewLimit.bind(this)
}
componentDidUpdate (prevProps, prevState) {
const { listSize } = this.state
// Our component has changed the list size.
// It needs to kick off a request for the first batch of new rows.
// InfiniteLoader may not do this because it memoizes calls to `loadMoreRows`.
if (
listSize > 0 &&
listSize !== prevState.listSize
) {
this._loadMoreRows({ startIndex: 0 })
}
}
componentWillUnmount () {
this._clearPendingTimeouts()
}
render () {
const { numLoadingRows } = this.state
const rowCount = this._getRowCount()
const loadMoreRows = numLoadingRows > 0 ? noop : this._loadMoreRows
return (
<ContentBox>
<ContentBoxParagraph>
<div>
<button onClick={() => this._setNewLimit(0)}>
Clear Data
</button>
<button onClick={() => this._setNewLimit(1)}>
Only One
</button>
<button onClick={() => this._setNewLimit(2)}>
Only Two
</button>
<button onClick={() => this._setNewLimit(100)}>
Multi-Page
</button>
</div>
</ContentBoxParagraph>
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={loadMoreRows}
minimumBatchSize={0}
rowCount={rowCount}
threshold={0}
>
{({ onRowsRendered, registerChild }) => (
<FlexTable
ref={( ref ) => {
this._flexTable = ref
registerChild(ref)
}}
onRowsRendered={onRowsRendered}
height={500}
width={500}
rowCount={rowCount}
rowGetter={this._rowGetter}
rowHeight={30}
disableHeader={true}
>
<FlexColumn
label='Name'
dataKey='name'
width={100}
flexGrow={2}
/>
</FlexTable>
)}
</InfiniteLoader>
</ContentBox>
)
}
_clearPendingTimeouts () {
Object.keys(this._timeoutIdMap).forEach(
(timeoutId) => clearTimeout(timeoutId)
)
}
_getRowCount () {
const { listSize, loadedRowCount, numLoadingRows } = this.state
// IF we are loading rows now, render placeholders for those loading rows
if (numLoadingRows > 0) {
return loadedRowCount + numLoadingRows
// If we have more rows to load, render a placeholder (unloaded row) to trigger the next batch
} else if (loadedRowCount < listSize) {
return loadedRowCount + 1
// If we've loaded all rows, render the full list
} else {
return listSize
}
}
_isRowLoaded ({ index }) {
return !!this._loadedRowsMap[index]
}
_loadMoreRows ({ startIndex }) {
const { listSize } = this.state
// InfiniteLoader will only request 1 row at a time because of our placeholder approach
// So we need to calculate our own batch size
const stopIndex = Math.min(listSize - 1, startIndex + ROWS_TO_FETCH_AT_A_TIME)
this.setState({
numLoadingRows: stopIndex - startIndex + 1
})
const timeout = 500 + Math.round( Math.random() * 2000 )
return new Promise((resolve) => {
const timeoutId = setTimeout(() => {
for (let index = startIndex; index <= stopIndex; index++) {
this._loadedRowsMap[index] = true
}
delete this._timeoutIdMap[timeoutId]
this.setState({
loadedRowCount: stopIndex + 1,
numLoadingRows: 0,
})
resolve()
}, timeout)
this._timeoutIdMap[timeoutId] = true
})
}
_rowGetter ({ index }) {
const { list } = this.props
const { listSize } = this.state
if (index === listSize) {
return { name: '༼ つ ◕_◕ ༽つ (placeholder)' }
} else if (this._isRowLoaded({ index })) {
return list.get( index )
} else {
return { name: 'Loading ...' }
}
}
_setNewLimit (listSize) {
this._clearPendingTimeouts()
this._loadedRowsMap = {}
this.setState({
listSize,
loadedRowCount: 0,
numLoadingRows: 0
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment