Tree数据处理
文章目录
- 一、Tree数据重置
- 二、Tree拆分成二级数据
- 1、过滤数据
- 2、二级数据
Tree组件的数据处理往往需要使用递归,本文归纳一下常见的数据处理情景,持续更新;
一、Tree数据重置
- 递归的标志就是寻找子元素的集合字段,一般为children,将所有节点依次过滤,
- 遍历过程类似于先序遍历,递归得到的返回值重新组成新的children数据,这个过程类似于后序遍历
- 遍历过程主要是增加必要的属性比如value、key,还可以根据节点数据动态设置icon
- 注意展开项expandedKeys的收集,根据数据自主控制
import { SmileOutlined } from "@ant-design/icons";
import { Button, Form, Tree } from "antd";
import React, { useMemo } from "react";
const treeDataTest = [{ name: "0", id: "0", children: [{ name: "1", id: "0-0" }] },{name: "1",id: "1",sub: [{name: "1-0",id: "1-0",children: [{ name: "1-0-0", id: "1-0-0" },{ name: "1-0-1", id: "1-0-1" }]},{name: "2-0",id: "2-0",sub: [{name: "2-0-0",id: "2-0-0",sub: [{ name: "2-0-0-0", id: "2-0-0-0", children: [{ name: "2-0-0-0-0", id: "2-0-0-0-0" }] }]}]}]}
];
export default function TreePage() {const [form] = Form.useForm();const [expandedKeys, setExpandedKeys] = React.useState([]);const labelWarpBtn = {offset: 6,span: 14};const onValuesChange = (changedValues, allValues) => {console.log("changedValues: ", changedValues);console.log("allValues: ", allValues);};// 转换每一个节点,只区分children、sub属性、icon属性const transformData = (data, expandedKeys) => {data.forEach((item) => {item.title = item.name;item.key = item.id;if (item.children) {expandedKeys.push(item.key);item.children = transformData(item.children, expandedKeys);} else if (item.sub) {item.children = transformData(item.sub, expandedKeys);} else {item.icon = <SmileOutlined />;}});return data;};// 单纯过滤数据添加属性const treeData = useMemo(() => {const expandedKeys = [];const data = transformData(treeDataTest, expandedKeys);setExpandedKeys(expandedKeys);return data;}, [treeDataTest]);return (<div><Form form={form} labelCol={{ span: 6 }} wrapperCol={{ span: 14 }} onValuesChange={onValuesChange}><Form.Item name="tree" label="tree">{/* fieldNames={{ title: "name", key: "id", children: "children" }} */}{/* 这里只是初步定义,只能扩充三个字段,想要更灵活的属性,在数据层修改就好了 */}{/* 一般处理数据后还要关注expandedKeys等属性 */}<Tree treeData={treeData} expandedKeys={expandedKeys} onExpand={setExpandedKeys} showIcon></Tree></Form.Item><Form.Item wrapperCol={labelWarpBtn}><Button type="primary" htmlType="submit" onClick={console.log(form.getFieldsValue())}>Submit</Button></Form.Item></Form></div>);
}
二、Tree拆分成二级数据
1、过滤数据
- 过滤不存在有效数据的节点,假设num表示该节点下级存在的有效数据的数量,通过num可以进行空数据过滤
- 递归中遇到报错可以使用debugger查看问题,调用次数太多使用console也无法定位
const treeDataTest = [{ name: "0", id: "0", children: [{ name: "1", id: "0-0" }] },{name: "1",id: "1",sub: [{name: "1-0",id: "1-0",children: [{ name: "1-0-0", id: "1-0-0" },{ name: "1-0-1", id: "1-0-1" }]},{name: "2-0",id: "2-0",sub: [{name: "2-0-0",id: "2-0-0",sub: [{ name: "2-0-0-0", id: "2-0-0-0", children: [{ name: "2-0-0-0-0", id: "2-0-0-0-0" }] }]}]}]}
];// 过滤数据,只展示存在有效数据的节点const filterData = (data) => {const filter = (arr) => {return arr.filter((item) => {if (item.num > 0) {// debugger;if (item.sub?.length > 0) {item.sub = filter(item.sub);}return true;}return false;});};return filter(JSON.parse(JSON.stringify(data)));};
2、二级数据
- 当需要拆分成两级时,需要把中间层级省略,保留末端children数据(假设有效数据都保存在children中)
- 设定目标数据的层级为两级,就可以遍历最外层,而内层递归,逐个往数组里添加末端children数据
import { SmileOutlined } from "@ant-design/icons";
import { Button, Form, Tree } from "antd";
import React, { useCallback, useMemo } from "react";
const treeDataTest = [{ name: "0", id: "0", num: 1, children: [{ name: "1", id: "0-0" }], sub: [] },{name: "1",id: "1",num: 3,sub: [{name: "1-0",id: "1-0",num: 2,children: [{ name: "1-0-0", id: "1-0-0" },{ name: "1-0-1", id: "1-0-1" }]},{name: "2-0",id: "2-0",num: 1,sub: [{name: "2-0-0",id: "2-0-0",num: 1,sub: [{ name: "2-0-0-0", id: "2-0-0-0", num: 1, children: [{ name: "2-0-0-0-0", id: "2-0-0-0-0" }] }]}]}]},{name: "2",id: "2",num: 0,sub: [{ name: "2-0", id: "2-0", num: 0, sub: [{ name: "2-0-0", id: "2-0-0", num: 0 }] }]}
];
export default function TreePage() {const [form] = Form.useForm();const [expandedKeys, setExpandedKeys] = React.useState([]);const labelWarpBtn = {offset: 6,span: 14};const onValuesChange = (changedValues, allValues) => {console.log("changedValues: ", changedValues);console.log("allValues: ", allValues);};// 转换为二级树结构,方便展示数据const transformChildrenOnly = (data) => {data.forEach((item) => {item.title = item.name;item.key = item.id;item.icon = <SmileOutlined />;});return data;};const transformChildren = (data, arr) => {data.forEach((item) => {// 这里递归的条件仅限于sub,因为他是叶子节点,不被需要// 如果有level层级,可以采取更灵活的条件去拆分数据if (item.children) {arr.push(...transformChildrenOnly(item.children));} else if (item.sub) {transformChildren(item.sub, arr);}});return data;};const transformDataToSecondTree = useCallback((data) => {const newData = [];const expandedKeys = [];data.forEach((item) => {const arr = [];item.title = item.name;item.key = item.id;expandedKeys.push(item.key);if (item.children) {// 如果第二层就是childrenarr.push(...transformChildrenOnly(item.children));} else if (item.sub) {// 如果第二层是sub属性,sub代表他是叶子节点,不是最终节点transformChildren(item.sub, arr);}newData.push({ ...item, children: arr });});setExpandedKeys(expandedKeys);return newData;}, []);// 过滤数据,只展示存在有效数据的节点const filterData = (data) => {const filter = (arr) => {return arr.filter((item) => {if (item.num > 0) {// debugger;if (item.sub?.length > 0) {item.sub = filter(item.sub);}return true;}return false;});};return filter(JSON.parse(JSON.stringify(data)));};// 转换为二级树结构const treeData = useMemo(() => transformDataToSecondTree(filterData(treeDataTest)), [treeDataTest]);return (<div><Form form={form} labelCol={{ span: 6 }} wrapperCol={{ span: 14 }} onValuesChange={onValuesChange}><Form.Item name="tree" label="tree">{/* fieldNames={{ title: "name", key: "id", children: "children" }} */}{/* 这里只是初步定义,只能扩充三个字段,想要更灵活的属性,在数据层修改就好了 */}{/* 一般处理数据后还要关注expandedKeys等属性 */}<Tree treeData={treeData} expandedKeys={expandedKeys} onExpand={setExpandedKeys} showIcon></Tree></Form.Item><Form.Item wrapperCol={labelWarpBtn}><Button type="primary" htmlType="submit" onClick={console.log(form.getFieldsValue())}>Submit</Button></Form.Item></Form></div>);
}