react-vtree
Version:
React component for efficiently rendering large tree structures
144 lines (125 loc) • 3.61 kB
JavaScript
/* eslint-disable react/no-unused-state,@typescript-eslint/consistent-type-assertions */
import React, { PureComponent } from 'react';
// eslint-disable-next-line @typescript-eslint/naming-convention,@typescript-eslint/prefer-readonly-parameter-types
export const Row = ({
index,
data: {
component: Node,
treeData,
order,
records
},
style,
isScrolling
}) => /*#__PURE__*/React.createElement(Node, Object.assign({}, records[order[index]], {
style: style,
isScrolling: isScrolling,
treeData: treeData
}));
export const createTreeComputer = ({
createRecord,
shouldUpdateRecords,
updateRecord,
updateRecordOnNewData
}) => ({
treeWalker
}, state, options = {}) => {
var _options$refreshNodes;
const order = [];
const records = { ...state.records
};
const iter = treeWalker((_options$refreshNodes = options.refreshNodes) != null ? _options$refreshNodes : false); // Here we are updating all records to the provided openness state described
// in UpdateOptions. It should be done before the tree re-calculation
// because tree walker omits closed nodes while update is required for all
// of them.
if (shouldUpdateRecords(options)) {
for (const id in records) {
updateRecord(records[id], id, options);
}
}
let isPreviousOpened = false; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition,no-constant-condition
while (true) {
const {
done,
value
} = iter.next(isPreviousOpened);
if (done || !value) {
break;
}
let id;
if (typeof value === 'string' || typeof value === 'symbol') {
id = value;
} else {
({
id
} = value);
const record = records[id];
if (!record) {
records[id] = createRecord(value, options, state);
} else {
record.data = value;
updateRecordOnNewData(record, options);
}
}
if (records[id]) {
order.push(id);
isPreviousOpened = records[id].isOpen;
} else if (process.env.NODE_ENV === 'development') {
// eslint-disable-next-line no-console
console.error(`No record with id ${String(id)} found.`);
}
}
return {
order,
records
};
};
class Tree extends PureComponent {
static getDerivedStateFromProps(props, state) {
const {
children: component,
itemData: treeData,
treeWalker
} = props;
const {
computeTree,
order,
treeWalker: oldTreeWalker
} = state;
return {
component,
treeData,
treeWalker,
...(treeWalker !== oldTreeWalker || !order ? computeTree(props, state, {
refreshNodes: true
}) : null)
};
}
constructor(props, context) {
super(props, context);
this.list = /*#__PURE__*/React.createRef();
this.state = {
component: props.children,
recomputeTree: this.recomputeTree.bind(this),
records: {}
};
}
recomputeTree(options) {
return new Promise(resolve => {
this.setState(prevState => prevState.computeTree(this.props, prevState, options), resolve);
});
}
scrollTo(scrollOffset) {
var _this$list$current;
(_this$list$current = this.list.current) == null ? void 0 : _this$list$current.scrollTo(scrollOffset);
}
scrollToItem(id, align) {
var _this$list$current2;
// eslint-disable-next-line react/destructuring-assignment
(_this$list$current2 = this.list.current) == null ? void 0 : _this$list$current2.scrollToItem(this.state.order.indexOf(id), align);
}
}
Tree.defaultProps = {
rowComponent: Row
};
export default Tree;