Created
May 14, 2016 20:20
-
-
Save bvaughn/3a358dda3654e1e93fba35890a093c19 to your computer and use it in GitHub Desktop.
Example Grid with columns and rows, built using :cellRangeRenderer (with react-virtualized 7.x)
This file contains 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 Immutable from 'immutable' | |
import React, { Component, PropTypes } from 'react' | |
import { ContentBox, ContentBoxHeader, ContentBoxParagraph } from '../demo/ContentBox' | |
import { LabeledInput, InputRow } from '../demo/LabeledInput' | |
import AutoSizer from '../AutoSizer' | |
import Grid from './Grid' | |
import shallowCompare from 'react-addons-shallow-compare' | |
import cn from 'classnames' | |
import styles from './Grid.example.css' | |
const FIXED_CELL_ZINDEX = 2 | |
const FIXED_LEFT_COLUMN_WIDTH = 50 | |
const FIXED_TOP_ROW_HEIGHT = 40 | |
export default class GridExample extends Component { | |
static propTypes = { | |
list: PropTypes.instanceOf(Immutable.List).isRequired | |
} | |
constructor (props, context) { | |
super(props, context) | |
this.state = { | |
columnWidth: 100, | |
columnCount: 1000, | |
height: 300, | |
rowHeight: 40, | |
rowCount: 1000, | |
scrollToColumn: undefined, | |
scrollToRow: undefined, | |
useDynamicRowHeight: false | |
} | |
this._cellRangeRenderer = this._cellRangeRenderer.bind(this) | |
this._cellRenderer = this._cellRenderer.bind(this) | |
this._getColumnWidth = this._getColumnWidth.bind(this) | |
this._getRowClassName = this._getRowClassName.bind(this) | |
this._getRowHeight = this._getRowHeight.bind(this) | |
} | |
render () { | |
const { | |
columnCount, | |
height, | |
rowHeight, | |
rowCount, | |
scrollToColumn, | |
scrollToRow, | |
useDynamicRowHeight | |
} = this.state | |
return ( | |
<ContentBox {...this.props}> | |
<ContentBoxHeader | |
text='Grid' | |
sourceLink='https://github.com/bvaughn/react-virtualized/blob/master/source/Grid/Grid.example.js' | |
docsLink='https://github.com/bvaughn/react-virtualized/blob/master/docs/Grid.md' | |
/> | |
<AutoSizer disableHeight> | |
{({ width }) => ( | |
<Grid | |
cellRangeRenderer={this._cellRangeRenderer} | |
cellRenderer={this._cellRenderer} | |
className={styles.BodyGrid} | |
columnWidth={this._getColumnWidth} | |
columnCount={columnCount} | |
height={height} | |
overscanColumnCount={0} | |
overscanRowCount={0} | |
rowHeight={useDynamicRowHeight ? this._getRowHeight : rowHeight} | |
rowCount={rowCount} | |
scrollToColumn={scrollToColumn} | |
scrollToRow={scrollToRow} | |
width={width} | |
/> | |
)} | |
</AutoSizer> | |
</ContentBox> | |
) | |
} | |
shouldComponentUpdate (nextProps, nextState) { | |
return shallowCompare(this, nextProps, nextState) | |
} | |
// Forked from defaultCellRangeRenderer.js | |
_cellRangeRenderer ({ | |
cellCache, | |
cellRenderer, | |
columnSizeAndPositionManager, | |
columnStartIndex, | |
columnStopIndex, | |
isScrolling, | |
rowSizeAndPositionManager, | |
rowStartIndex, | |
rowStopIndex, | |
scrollLeft, | |
scrollTop | |
}) { | |
const renderedCells = [] | |
// Top-left corner piece | |
renderedCells.push( | |
<div | |
key='fixed-fixed' | |
className={cn('Grid__cell', styles.cell, styles.topLeftCell)} | |
style={{ | |
height: FIXED_TOP_ROW_HEIGHT, | |
left: scrollLeft, | |
position: 'fixed', | |
top: scrollTop, | |
width: FIXED_LEFT_COLUMN_WIDTH, | |
zIndex: FIXED_CELL_ZINDEX + 1 | |
}} | |
> | |
| |
</div> | |
) | |
// Render fixed header row | |
for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) { | |
let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex) | |
renderedCells.push( | |
<div | |
key={`fixed-${columnIndex}`} | |
className={cn('Grid__cell', styles.cell, styles.headerCell)} | |
style={{ | |
height: FIXED_TOP_ROW_HEIGHT, | |
left: columnDatum.offset + FIXED_LEFT_COLUMN_WIDTH, | |
position: 'fixed', | |
top: scrollTop, | |
width: columnDatum.size, | |
zIndex: FIXED_CELL_ZINDEX | |
}} | |
> | |
H{columnIndex} | |
</div> | |
) | |
} | |
// Render fixed left column | |
for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) { | |
let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex) | |
let datum = this._getDatum(rowIndex) | |
renderedCells.push( | |
<div | |
key={`${rowIndex}-fixed`} | |
className={cn('Grid__cell', styles.cell, styles.letterCell)} | |
style={{ | |
backgroundColor: datum.color, | |
height: rowDatum.size, | |
left: scrollLeft, | |
position: 'fixed', | |
top: rowDatum.offset + FIXED_TOP_ROW_HEIGHT, | |
width: FIXED_LEFT_COLUMN_WIDTH, | |
zIndex: FIXED_CELL_ZINDEX | |
}} | |
> | |
{datum.name.charAt(0)} | |
</div> | |
) | |
} | |
// Forked from defaultCellRangeRenderer.js | |
for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) { | |
let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex) | |
for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) { | |
let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex) | |
let key = `${rowIndex}-${columnIndex}` | |
let renderedCell | |
// Avoid re-creating cells while scrolling. | |
// This can lead to the same cell being created many times and can cause performance issues for "heavy" cells. | |
// If a scroll is in progress- cache and reuse cells. | |
// This cache will be thrown away once scrolling complets. | |
if (isScrolling) { | |
if (!cellCache[key]) { | |
cellCache[key] = cellRenderer({ | |
columnIndex, | |
isScrolling, | |
rowIndex | |
}) | |
} | |
renderedCell = cellCache[key] | |
// If the user is no longer scrolling, don't cache cells. | |
// This makes dynamic cell content difficult for users and would also lead to a heavier memory footprint. | |
} else { | |
renderedCell = cellRenderer({ | |
columnIndex, | |
isScrolling, | |
rowIndex | |
}) | |
} | |
if (renderedCell == null || renderedCell === false) { | |
continue | |
} | |
let child = ( | |
<div | |
key={key} | |
className='Grid__cell' | |
style={{ | |
height: rowDatum.size, | |
left: columnDatum.offset + FIXED_LEFT_COLUMN_WIDTH, | |
position: 'fixed', | |
top: rowDatum.offset + FIXED_TOP_ROW_HEIGHT, | |
width: columnDatum.size | |
}} | |
> | |
{renderedCell} | |
</div> | |
) | |
renderedCells.push(child) | |
} | |
} | |
return renderedCells | |
} | |
_cellRenderer ({ columnIndex, rowIndex }) { | |
const rowClass = this._getRowClassName(rowIndex) | |
const datum = this._getDatum(rowIndex) | |
let content | |
switch (columnIndex) { | |
case 0: | |
content = datum.name | |
break | |
case 1: | |
content = datum.random | |
break | |
default: | |
content = ( | |
<div> | |
c:{columnIndex} | |
<br /> | |
r:{rowIndex} | |
</div> | |
) | |
break | |
} | |
const classNames = cn(rowClass, styles.cell, { | |
[styles.centeredCell]: columnIndex > 2 | |
}) | |
return ( | |
<div className={classNames}> | |
{content} | |
</div> | |
) | |
} | |
_getColumnWidth ({ index }) { | |
switch (index) { | |
case 0: | |
return 100 | |
case 1: | |
return 300 | |
default: | |
return 50 | |
} | |
} | |
_getDatum (index) { | |
const { list } = this.props | |
return list.get(index % list.size) | |
} | |
_getRowClassName (row) { | |
return row % 2 === 0 ? styles.evenRow : styles.oddRow | |
} | |
_getRowHeight ({ index }) { | |
return this._getDatum(index).size | |
} | |
} |
The fixed cells flicker because scrollLeft, scrollTop
are not updated smoothly by the browser.
Using pure logic scrolling seems to be the only way to resolve the flickering and achieve a smooth scrolling experience.
I ended up using
https://github.com/gooddata/zynga-scroller-es6 and wrapped the grid in a component similar to https://github.com/facebook/fixed-data-table/blob/master/site/examples/TouchableArea.js
Hey @vinayaknagpal. Any chance you'd be willing to share a Gist or Plunker of your react-virtualized + zynga-scroller-es6 combination? 😄
@bvaughn
Sure. Will put together an example and share in a few days. Racing a release deadline at the moment.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If anyone gets to this example and they are facing scrolling issues, its probably because of the
position: fixed
property in the_cellRangeRenderer
cells. Update those topostion: absolute
and scrolling should be goodPlaces where it needs updating -