Open cisen opened 5 years ago
edittable测试实现
/* eslint-disable max-classes-per-file */
/* eslint-disable react/no-multi-comp */
import React from 'react';
import memoizeOne from 'memoize-one';
import { isFunction, get, merge, isEqual } from 'lodash/fp';
import { cancelTimeout, requestTimeout } from './utils/timer';
const IS_SCROLLING_DEBOUNCE_INTERVAL = 150;
function getStartIndexForOffset({ itemCount, itemSize }, offset) {
return Math.max(0, Math.min(itemCount - 1, Math.floor(offset / itemSize)));
}
function getStopIndexForStartIndex({ itemCount, itemSize, height }, startIndex, scrollOffset) {
const offset = startIndex * itemSize;
return Math.max(0, Math.min(itemCount - 1, startIndex + Math.floor((height + (scrollOffset - offset)) / itemSize)));
}
function setEditTable(Table) {
class EditableTable extends React.PureComponent {
constructor(props) {
super(props);
this.resetIsScrollingTimeoutId = null;
this.instanceProps = {};
this.scrollWrapperDom = document.querySelector('.ir-content');
// 对于垂直列表,这是行高。 对于水平列表,这是列宽。
this.itemSize = 60;
// 条数的总数
// itemCount
this.overscanCount = 10;
// 窗口的高度
this.height = 40;
this.state = {
// instance: this,
isScrolling: false,
scrollDirection: 'forward',
// 初始化滚动位置
scrollOffset: typeof props.initialScrollOffset === 'number' ? props.initialScrollOffset : 0,
scrollUpdateWasRequested: false,
};
}
componentDidMount() {
this.scrollWrapperDom.addEventListener('scroll', this.onScrollVertical);
}
componentDidUpdate() {
const { scrollOffset, scrollUpdateWasRequested } = this.state;
// if (scrollUpdateWasRequested && this.scrollWrapperDom !== null) {
// this.scrollWrapperDom.scrollTop = scrollOffset;
// }
}
// 垂直滚动事件
onScrollVertical = event => {
const { scrollTop } = event.currentTarget;
this.setState(prevState => {
if (prevState.scrollOffset === scrollTop) {
// Scroll position may have been updated by cDM/cDU,
// In which case we don't need to trigger another render,
// And we don't want to update state.isScrolling.
return null;
}
return {
isScrolling: true,
scrollDirection: prevState.scrollOffset < scrollTop ? 'forward' : 'backward',
scrollOffset: scrollTop,
scrollUpdateWasRequested: false,
};
}, this.resetIsScrollingDebounced);
};
resetIsScrollingDebounced = () => {
if (this.resetIsScrollingTimeoutId !== null) {
cancelTimeout(this.resetIsScrollingTimeoutId);
}
this.resetIsScrollingTimeoutId = requestTimeout(this.resetIsScrolling, IS_SCROLLING_DEBOUNCE_INTERVAL);
};
resetIsScrolling = () => {
this.resetIsScrollingTimeoutId = null;
this.setState({ isScrolling: false }, () => {
// Clear style cache after state update has been committed.
// This way we don't break pure sCU for items that don't use isScrolling param.
this.getItemStyleCache(-1, null);
});
};
getItemStyleCache = memoizeOne(() => {});
handleSave = row => {
this.props.onEditSave(row);
};
getRangeToRender = () => {
const { dataSource } = this.props;
const { isScrolling, scrollDirection, scrollOffset } = this.state;
const overscanCount = this.overscanCount;
const itemCount = (dataSource && dataSource.length) || 0;
const itemSize = this.itemSize;
const height = this.height;
if (itemCount === 0) {
return [0, 0, 0, 0];
}
const startIndex = getStartIndexForOffset({ itemCount, itemSize }, scrollOffset, this.instanceProps);
const stopIndex = getStopIndexForStartIndex(
{ itemCount, itemSize, height },
startIndex,
scrollOffset,
this.instanceProps,
);
// Overscan by one item in each direction so that tab/focus works.
// If there isn't at least one extra item, tab loops back around.
const overscanBackward = !isScrolling || scrollDirection === 'backward' ? Math.max(1, overscanCount) : 1;
const overscanForward = !isScrolling || scrollDirection === 'forward' ? Math.max(1, overscanCount) : 1;
return [
Math.max(0, startIndex - overscanBackward),
Math.max(0, Math.min(itemCount - 1, stopIndex + overscanForward)),
startIndex,
stopIndex,
];
}
onRowCb = (startIndex, stopIndex) => {
return (record, index) => {
const tools = this.props.tools || this.props.renderTools || this.props.renderLeftTools;
const { onRow } = this.props;
const custom = onRow ? onRow(record, index) : {};
return {
...custom,
tools,
startIndex,
stopIndex,
};
};
};
// onRow = (record, index) => {
// const tools = this.props.tools || this.props.renderTools || this.props.renderLeftTools;
// const { onRow } = this.props;
// const custom = onRow ? onRow(record, index) : {};
// return {
// ...custom,
// tools,
// };
// };
render() {
const { columns, components, dataSource, rowKey, ...restProps } = this.props;
const [startIndex, stopIndex] = this.getRangeToRender();
console.log('edit render', startIndex, stopIndex);
// const spliceDate = dataSource && dataSource.slice(startIndex, stopIndex);
const componentsEdit = {
body: {
cell: EditableCell,
},
};
const finalColumns = columns.map(col => {
if (!isFunction(col.edit)) {
return col;
}
return {
...col,
onCell: (record, rowIndex) => {
const editConf = col.edit(record);
return {
record,
rowIndex,
editable: editConf.editable,
inputEl: editConf.inputEl,
value: editConf.value,
getProps: editConf.getProps,
dataIndex: col.dataIndex,
title: col.title,
width: editConf.editableWidth ?? '100%',
content: editConf.popoverContent,
onSave: this.handleSave,
};
},
};
});
return (
<div>
<Table
{...restProps}
columns={finalColumns}
onRow={this.onRowCb(startIndex, stopIndex)}
components={merge(components)(componentsEdit)}
dataSource={dataSource}
rowClassName={() => 'ir-editable-row'}
rowKey={rowKey}
/>
</div>
);
}
}
return EditableTable;
}
export default setEditTable;
说明
https://github.com/cisen/sourcecode-react-window https://github.com/bvaughn/react-window
记录
触发滚动_onScroll时,只是很简单地setstate一下滚动方向和位置,还有缓存一份数据。真正执行滚动和滚动运算的入口是componentDidUpdate
计算渲染哪条到哪条的是_getHorizontalRangeToRender
isScrolling控制着是前进方向加载多少条,后退方向加载多少条
问题
-基本原理是什么?
3.
isScrolling
利用requestAnimationFrame在空闲的时候才设置为false