Created
August 27, 2016 15:38
-
-
Save bvaughn/9c7af838646942106a2ff689bbc7a1ff to your computer and use it in GitHub Desktop.
Response to bvaughn/react-virtualized#361
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** @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