组件
Collection
Grid
Grid是一个窗口网格。Grid网格只根据当前的水平/垂直滚动位置渲染填充自身所需的单元格。
Props
| 属性 | 类型 | 必选? | 描述 |
|---|---|---|---|
| autoContainerWidth | Boolean | 否 | 将内部可滚动的容器宽度设为自动。 |
| autoHeight | Boolean | 否 | Grid外部的高度设置为自动。这个属性只能和WindowScroller高阶组件结合使用 |
| autoWidth | Boolean | 否 | Grid外部的宽度设置为自动。这个属性只能和WindowScroller高阶组件结合使用 |
| cellRangeRenderer | Function | 否 | 负责渲染一组给定索引范围的单元格 |
| cellRenderer | Function | 是 | 负责渲染给定行列索引的单元格 |
| className | String | 否 | 可选的自定义CSS类名,附加到根Grid元素上 |
| columnCount | Number | 是 | 网格列的数量 |
| columnWidth | Number or Function | 是 | 一个固定的列宽(数字)或一个函数返回给定索引(({index: number}): number)的列宽。若使用函数,可以指定estimatedColumnSize来优化滚动行为。 |
| containerProps | Object | 否 | 负责添加属性到cell-container(例如onWheel事件) |
| containerRole | String | 否 | cell-ContainerARIA角色,默认是rowgroup |
| containerStyle | Object | 否 | 可选的自定义行内样式附加内部的cell-container元素 |
| deferredMeasurementCache | CellMeausrer | 否 | 若CellMeasure用于计算Grid的子元素,该属性必须是CellMeausrerCache的指针。一个共享的CellMeasurerCache引用使Grid和CellMeasurer共享计算数据 |
| estimatedColumnSize | Number | 否 | 用于在所有列被计算之前预估整个Grid的宽度。被预估的整宽会在列渲染后调整 |
| estimatedRowSize | Number | 否 | 用于在所有行被计算之前预估整个Grid的高度。被预估的整高会在行渲染后调整 |
| height | Number | 是 | Grid的高度;该属性决定了可见的行数量 |
| id | String | 否 | 可选的自定义ID来附加到根Grid元素 |
| isScrolling | Boolean | 否 | 重写内部是否处于滚动的状态,整个属性主要服务于WindowScroller组件 |
| isScrollingOptOut | Boolean | 否 | 防止在滚动端重新渲染可见单元格 |
| onContentRenderer | Function | 否 | 使用刚渲染的Grid区域的信息调用回调函数,旨在可见行发生改变时调用:({columnOverscanStartIndex: number, columnOverscanStopIndex: number, columnStartIndex: number, columnStopIndex: number, rowOverscanStartIndex: number, rowOverscanStopIndex: number, rowStartIndex: number, rowStopIndex: number}): void |
| onScroll | Function | 否 | 每当内部可滚动区域内的滚动偏移发生变化时,将调用回调:({ clientHeight: number, clientWidth: number, scrollHeight: number, scrollLeft: number, scrollTop: number, scrollWidth: number }): void |
| onScrollbarPresenceChange | Function | 否 | 每当添加或移除水平或垂直滚动条时调用:({ horizontal: boolean, size: number, vertical: boolean }): void |
| overscanColumnCount | Number | 否 | 渲染可见的网格切片前后的列数量。可以减少在某些浏览器或设备滚动期间的闪烁。 |
| overscanIndicesGetter | Function | 否 | 负责计算在指定范围之前和之后要扫描的单元格数量 |
| overscanRowCount | Number | 否 | 渲染可见的网格切片前后的行数量。可以减少在某些浏览器或设备滚动期间的闪烁。 |
| role | String | 否 | 可选的重写默认ARIA,默认是grid |
| rowCount | Number | 是 | 网格中行的数量 |
| rowHeight | Number or Function | 是 | 固定的行高(数字)或一个函数返回给定索引的行高:({index: nmber}): number。如果使用函数,指定estimatedRowSize来优化滚动行为 |
| scrollingResetTimeInterval | Number | 否 | 在上一次滚动事件之后等待此时间,然后重置网格指针事件;默认为150ms。 |
| scrollLeft | Number | 否 | 水平偏移量 |
| scrollToAlignment | String | 否 | 控制scrolled-to-cells的对齐方式。默认(自动)滚动到可能的最小数量确保指定单元格完全可见。使用start将指定单元格对其到网格左上,使用end对齐到右下。使用center将指定单元格对其到容器中间。 |
| scrollToColumn | Number | 否 | 确保可见的列索引(必要时强行滚动),优先于scrollLeft |
| scrollToRow | Number | 否 | 确保可见的行索引。优先于scrollTop |
| scrollTop | Number | 否 | 垂直偏移量 |
| style | Object | 否 | 可选的自定义内联样式,附加到根Grid元素 |
| tabIndex | Number | 否 | 可选的重写默认的标签索引。默认是0 |
| width | Nubmer | 是 | Grid的宽度,这个属性决定了可见的列的数量 |
公共方法
getOffsetForCell({alignment?: string, coumnIndex?: number, rowIndex?: number})
获取给定单元格的偏移量和对齐方式。
getTotalRowsHeight
获取预估的整行高度。
getTotalColumnWidth
获取预估的整列宽度
handleScrollEvent({scrollLeft, scrollTop})
此方法处理源于外部滚动控件的滚动事件。这是一种高级方法,除非您要实现自定义滚动条解决方案,否则可能不应该使用它。
measureAllCells
预计算所有Grid中的行和列。
recomputeGridSize({columnIndex: nubmer, rowIndex: number})
在指定索引后(默认是0)重新计算行高和列宽。
如果动态列或行的大小改变,但没有其他更改,则应调用此函数。因为Grid只接受columnCount和rowCount,它无法检测基础数据何时更改。
此方法还将强制执行渲染周期(通过forceUpdate),以确保更新的测量值反映在渲染的网格中。
scrollToCell(columnIndex: number, rowIndex: number)
确保行和列可见。这个方法可被用来当用户滚动远离某个单元格之后,安全地滚回到原处。即使已经滚动到了改位置。
scrollToPosition({scrollLeft, scrollTop})
滚动指定偏移量。用于设置位置变化的动画。
类名
Grid组件支持下列静态类名:
| 属性 | 描述 |
|---|---|
| ReactVirtualized__Grid | (外部)主要元素 |
| ReactVirtualizedGridinnerScrollContainer | 内部滚动区域 |
cellRangeRenderer
这是一个高级属性。当网格需要其他覆盖的UI(例如甘特图或日历应用程序)的情况下,它很有用。使用onScroll回调或ScrollSync 高阶组件可以更轻松地解决许多用例。
如果你想重写cellRangeRenderer,最简单的方法是增强默认的实现:
import {defaultCellRangeRenderer, Grid} from 'react-virtualized';
function cellRangeRenderer(props) {
const children = defaultCellRangeRenderer(props);
children.push(<div>My custom overlay</div>);
return children;
}
function CustomizedGrid(props) {
return <Grid cellRangeRenderer={cellRangeRenderer} {...props} />;
}
如果需要更大的自定义,则可能需要派生defaultCellRangeRenderer函数。
该函数接受以下命名参数:
function cellRangeRenderer({
cellCache, // Temporary cell cache used while scrolling
cellRenderer, // Cell renderer prop supplied to Grid
columnSizeAndPositionManager, // @see CellSizeAndPositionManager,
columnStartIndex, // Index of first column (inclusive) to render
columnStopIndex, // Index of last column (inclusive) to render
horizontalOffsetAdjustment, // Horizontal pixel offset (required for scaling)
isScrolling, // The Grid is currently being scrolled
rowSizeAndPositionManager, // @see CellSizeAndPositionManager,
rowStartIndex, // Index of first row (inclusive) to render
rowStopIndex, // Index of last row (inclusive) to render
scrollLeft, // Current horizontal scroll offset of Grid
scrollTop, // Current vertical scroll offset of Grid
styleCache, // Temporary style (size & position) cache used while scrolling
verticalOffsetAdjustment, // Vertical pixel offset (required for scaling)
}) {
const renderedCells = [];
for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) {
// This contains :offset (top) and :size (height) information for the cell
let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex);
for (
let columnIndex = columnStartIndex;
columnIndex <= columnStopIndex;
columnIndex++
) {
// This contains :offset (left) and :size (width) information for the cell
let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(
columnIndex,
);
// Be sure to adjust cell position in case the total set of cells is too large to be supported by the browser natively.
// In this case, Grid will shift cells as a user scrolls to increase cell density.
let left = columnDatum.offset + horizontalOffsetAdjustment;
let top = rowDatum.offset + verticalOffsetAdjustment;
// The rest of the information you need to render the cell are contained in the data.
// Be sure to provide unique :key attributes.
let key = `${rowIndex}-${columnIndex}`;
let height = rowDatum.size;
let width = columnDatum.size;
// Now render your cell and additional UI as you see fit.
// Add all rendered children to the :renderedCells Array.
}
}
return renderedCells;
}
overscanIndicesGetter
这是一个高级属性。这个函数负责计算指定范围前后扫描的单元格数量。默认下react-virtualized根据滚动方向优化了扫描的单元数。如果你想自定义这个行为,可以forkdefaultOverscanIndicesGetter函数。
function overscanIndicesGetter ({
direction, // One of "horizontal" or "vertical"
cellCount, // Number of rows or columns in the current axis
scrollDirection, // 1 (forwards) or -1 (backwards)
overscanCellsCount, // Maximum number of cells to over-render in either direction
startIndex, // Begin of range of visible cells
stopIndex // End of range of visible cells
}) {
return {
overscanStartIndex: Math.max(0, startIndex - overscanCellsCount),
overscanStopIndex: Math.min(cellCount - 1, stopIndex + overscanCellsCount)
}
}
cellRender
负责渲染单个单元格,给出了行列的索引。此函数接受下列命名参数:
function cellRenderer({
columnIndex, // Horizontal (column) index of cell
isScrolling, // The Grid is currently being scrolled
isVisible, // This cell is visible within the grid (eg it is not an overscanned cell)
key, // Unique key within array of cells
parent, // Reference to the parent Grid (instance)
rowIndex, // Vertical (row) index of cell
style, // Style object to be applied to cell (to position it);
// This must be passed through to the rendered cell element.
}) {
// Grid data is a 2d array in this example...
const user = list[rowIndex][columnIndex];
// If cell content is complex, consider rendering a lighter-weight placeholder while scrolling.
const content = isScrolling ? '...' : <User user={user} />;
// Style is required since it specifies how the cell is to be sized and positioned.
// React Virtualized depends on this sizing/positioning for proper scrolling behavior.
// By default, the grid component provides the following style properties:
// position
// left
// top
// height
// width
// You can add additional class names or style properties as you would like.
// Key is also required by React to more efficiently manage the array of cells.
return (
<div key={key} style={style}>
{content}
</div>
);
}
基础的Grid示例
下面是一个非常基础的Grid示例。这个网格展示了一组队具有固定行列大小的对象。(也支持动态的)。
import React from 'react';
import ReactDOM from 'react-dom';
import {Grid} from 'react-virtualized';
// Grid data as an array of arrays
const list = [
['Brian Vaughn', 'Software Engineer', 'San Jose', 'CA', 95125 /* ... */],
// And so on...
];
function cellRenderer({columnIndex, key, rowIndex, style}) {
return (
<div key={key} style={style}>
{list[rowIndex][columnIndex]}
</div>
);
}
// Render your grid
ReactDOM.render(
<Grid
cellRenderer={cellRenderer}
columnCount={list[0].length}
columnWidth={100}
height={300}
rowCount={list.length}
rowHeight={30}
width={300}
/>,
document.getElementById('example'),
);
List
Table
-
Column
-
SortDirection
高阶组件
ArrowKeyStepper
AutoSizer
CellMeasurer
CellMeasurer是通过一种用户不可见的渲染方式自动计算单元格内容的高阶组件。可以指定一个固定的宽度来计算动态的高度(反之亦然)。这是一个高级的组件,但具有一些局限性和性能方面的注意事项。
CellMeasurer可以和Grid,List和Table这些组件一起使用,但不可以和Collection一起使用。
Props
| 属性 | 类型 | 必选? | 描述 |
|---|---|---|---|
| cache | CellMeasurerCache | 是 | 在CellMeasure和他们的父级Grid之间共享的缓存 |
| children | Element or Function | 是 | 可以是一个React元素(例如<div />)或者一个函数(例如 ({measure, registerChild}) => <div ref={registerChild} />) |
| columnIndex | number | 是 | 经计算的列index(在父级Grid里)或者0(使用List或Table) |
| parent | Grid | 是 | 父级Grid的引用;改值是由Grid传递给cellRenderer,应该原样传递 |
| rowIndex | number | 是 | 经计算的行index(在父级Grid里) |
Render Props
| 属性 | 类型 | 描述 |
|---|---|---|
| measure | Function | 执行单元格计算 |
| registerChild | Function | 指定要计算的Dom元素,可以被用作为ref(默认是WindowScroller使用ReactDOM.findDOMNode函数) |
CellMeasurerCache
CellMeasurerCache存储了CellMeasurer的计算并且将他们共享与一个父级Grid。应当基于你所需要的计算类型来配置。接受以下参数:
| 属性 | 类型 | 必选? | 描述 |
|---|---|---|---|
| defaultHeight | number | 否 | 未计算的单元格将改值初始化高度 |
| defaultWidth | number | 否 | 未计算的单元格将改值初始化宽度 |
| fixedHeight | number | 否 | 渲染单元格将拥有一个固定的高度,动态的宽度 |
| fixedWidth | number | 否 | 渲染单元格将拥有一个固定的宽度,动态的高度 |
| minHeight | number | 否 | 多个单元格的派生行高度不应该小于改值 |
| minWidth | number | 否 | 多个单元格的派生列宽度不应该小于改值 |
| keyMapper | number | 否 | 可以更智能地将给定的列和行索引映射到项目ID。这样可以防止在修改其父级时单元格缓存失效。(rowIndex: number, columnIndex: number) => any |
注意上述所有属性都是可选的,你必须至少提供其中一些。CellMeasureerCache并不意味着同时计算动态地宽度和高度。当行(或列)的大小等于这个行最大单元格的大小时这样做会十分低效。
CellMeasurerCache通用的方法
clear(rowIndex: number, columnIndex: number)重置一个指定单元格的计算缓存
应当在单元格需要重新计算来处理动态地内容时调用。(例如用加载的内容替换加载图标或对有状态单元的状态变化做出响应)
clearAll()
重置所有单元格的计算缓存
应当在Grid,List,Table组件因使用响应式布局触发resize事件需要回流内容的时候调用。(例如修改窗口大小时需要一个单元格高度的响应)
例子
- Grid
这个例子展示了固定行高动态列宽的Grid。
import React from 'react';
import { CellMeasurer, CellMeasurerCache, Grid } from 'react-virtualized';
// In this example, average cell width is assumed to be about 100px.
// This value will be used for the initial `Grid` layout.
// Cell measurements smaller than 75px should also be rounded up.
// Height is not dynamic.
const cache = new CellMeasurerCache({
defaultWidth: 100,
minWidth: 75,
fixedHeight: true
});
function cellRenderer ({ columnIndex, key, parent, rowIndex, style }) {
const content // Derive this from your data somehow
return (
<CellMeasurer
cache={cache}
columnIndex={columnIndex}
key={key}
parent={parent}
rowIndex={rowIndex}
>
<div
style={{
...style,
height: 35,
whiteSpace: 'nowrap'
}}
>
{content}
</div>
</CellMeasurer>
);
}
function renderGrid (props) {
return (
<Grid
{...props}
columnWidth={cache.columnWidth}
deferredMeasurementCache={cache}
cellRenderer={cellRenderer}
/>
);
}
- 使用
registerChild
CellMeasurer默认使用findDOMNode来获取到DOM元素去计算。但这个API在React严格模式下是不推荐的。所以你可能想要避免这个用法。作为代替,你可以使用registerChild属性来指定元素。(例如将他作为一个ref传入)
import React from 'react';
import { CellMeasurer, CellMeasurerCache, Grid } from 'react-virtualized';
// In this example, average cell width is assumed to be about 100px.
// This value will be used for the initial `Grid` layout.
// Cell measurements smaller than 75px should also be rounded up.
// Height is not dynamic.
const cache = new CellMeasurerCache({
defaultWidth: 100,
minWidth: 75,
fixedHeight: true
});
function cellRenderer ({ columnIndex, key, parent, rowIndex, style }) {
const content // Derive this from your data somehow
return (
<CellMeasurer
cache={cache}
columnIndex={columnIndex}
key={key}
parent={parent}
rowIndex={rowIndex}
>
{({registerChild}) => (
<div
style={{
...style,
height: 35,
whiteSpace: 'nowrap'
}}
>
{content}
</div>
)}
</CellMeasurer>
);
}
function renderGrid (props) {
return (
<Grid
{...props}
columnWidth={cache.columnWidth}
deferredMeasurementCache={cache}
cellRenderer={cellRenderer}
/>
);
}
- 和图片一起使用
CellMeasurer
本示例说明如何将CellMeasurer组件与List组件一起使用,以显示动态高度行。与上述实例不同在于:行高是由图片数据加载完成后决定的。为此将一个函数传入CellMeasurer组件,接受measure和registerChild两个参数。measure应在单元格内容计算完后调用。(本例中为图片数据加载完成后)
import React from 'react';
import { CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
// In this example, average cell height is assumed to be about 50px.
// This value will be used for the initial `Grid` layout.
// Width is not dynamic.
const cache = new CellMeasurerCache({
defaultHeight: 50,
fixedWidth: true
});
function rowRenderer ({ index, isScrolling, key, parent, style }) {
const source // This comes from your list data
return (
<CellMeasurer
cache={cache}
columnIndex={0}
key={key}
parent={parent}
rowIndex={index}
>
{({ measure, registerChild }) => (
// 'style' attribute required to position cell (within parent List)
<div ref={registerChild} style={style}>
<img
onLoad={measure}
src={source}
/>
</div>
)}
</CellMeasurer>
);
}
function renderList (props) {
return (
<List
{...props}
deferredMeasurementCache={cache}
rowHeight={cache.rowHeight}
rowRenderer={rowRenderer}
/>
);
}
- 使用具有相同动态单元格高度或宽度的
CellMeasurer
// Normally, every cell gets measured individually and is very slow.
// However, with the keyMapper prop we can specify a constant return value and
// tell CellMeasurer that all measurements after the first one will hit the
// cache and we get a speedy solution.
const cache = new CellMeasurerCache({
defaultHeight: 30,
fixedWidth: true,
keyMapper: () => 1,
});
局限性和性能事项
计算列宽需要计算所有行来决定该列发生的最大宽度。计算行高亦如此。因此当列和单元格同时包含庞大的数据时,使用这个Grid的高阶组件并不是一个好的主意。
由于该组件一次只计算一个单元格的宽高,所以如果用户用滚动条或者scoll-to-cell属性一次性跳过许多行(或列)时将会卡慢。不幸的是现在没有针对该性能局限性的解决方案。