跳到主要内容

react-rc-tree

2023年11月10日
柏拉文
越努力,越幸运

一、认识


Tree 组件的核心思路是将原始的嵌套 children 数据结构平铺成一维数组,然后通过计算每个节点的深度(deep)、层级关系等信息,在渲染时动态计算缩进宽度、连接线等,从而实现树形结构的可视化。

1.1 Tree 组件如何实现高性能大数据渲染?

  1. 将原始树形数据平铺为一维数组,便于后续计算

  2. 计算出实际需要渲染的节点数据,过滤隐藏的节点

  3. 利用虚拟列表技术只渲染可视区域的数据,实现大数据量的高效渲染

function flattenTreeData(treeData = [], parent = null) {
const nodes = [];

treeData.forEach((node) => {
const newNode = {
...node,
parent,
};

nodes.push(newNode);

if (newNode.children) {
nodes.push(...flattenTreeData(newNode.children, newNode));
}
});

return nodes;
}

1.2 如何计算 Tree 组件中节点的各种状态(展开/折叠、选中等)?

  1. 展开/折叠状态根据ExpandedKeys计算

  2. 复选框选中状态需要考虑受控/非受控,严格受控模式,及父子节点关联

  3. 需要递归计算父节点和子节点的状态

  4. 利用平铺后的索引进行相关节点查询

function flattenTreeData(treeData = [], parent = null) {
const nodes = [];

treeData.forEach((node) => {
const newNode = {
...node,
parent,
};

nodes.push(newNode);

if (newNode.children) {
nodes.push(...flattenTreeData(newNode.children, newNode));
}
});

return nodes;
}

1.3 Tree 组件的交互如何实现?点击节点展开折叠,复选框状态切换等

  1. 点击展开折叠通过更新节点自身状态、可视状态及ExpandedKeys实现

  2. 点击复选框需要递归更新父子节点的状态,及相关keys

  3. 计算并保存实时状态,通过回调函数通知外部

function toggleExpanded(nodes, node) {
return nodes.map((currentNode) => {
if (currentNode === node) {
return {
...currentNode,
expanded: !currentNode.expanded,
};
}

return currentNode;
});
}

// 在渲染时计算缩进:
function renderNode(node) {
const indentLevel = getIndentLevel(node);
const style = {
paddingLeft: `${indentLevel * 16}px`,
};

return (
<div style={style} onClick={() => handleNodeClick(node)}>
{node.label}
</div>
);
}

二、实现


import Tree from './components/Tree/tree';

const treeData = [
{
title: '0-0-title',
key: '001',
children: [
{
title: '0-0-0-title',
key: '002',
children: [
{ title: '0-0-0-0-title', key: '003' },
{ title: '0-0-0-1-title', key: '004' },
{ title: '0-0-0-2-title', key: '005' }
]
},
{
title: '0-0-1-title',
key: '006',
children: [
{ title: '0-0-1-0-title', key: '007' },
{ title: '0-0-1-1-title', key: '008' },
{ title: '0-0-1-2-title', key: '009' }
]
},
{
title: '0-0-2-title',
key: '010'
}
]
},
{
title: '0-1-title',
key: '011',
children: [
{ title: '0-1-0-0', key: '012' },
{ title: '0-1-0-1', key: '013' },
{ title: '0-1-0-2', key: '014' }
]
},
{
title: '0-2-title',
key: '015'
}
];

function App() {
return (
<div>
<Tree treeData={treeData} />
</div>
);
}

export default App;