思考一个问题:如何获取河北和天津下一级的数据集合呢?
带着这个问题看下面↓↓↓树形结构
const tree = [
{
value: '1',
name: '河北',
children: [
{
value: '10',
name: '沧州',
children: [
{ value: '101', name: '沧县' },
{ value: '102', name: '泊头' },
{ value: '103', name: '衡水' }
]
}
]
},
{
value: '2',
name: '北京',
children: [
{ value: '20', name: '朝阳区'},
{ value: '21', name: '西城区', },
{ value: '22', name: '东城区', }
]
},
{
value: '3',
name: '天津',
children: [
{ value: '31', name: '武清区'},
{ value: '32', name: '津南区' },
{ value: '33', name: '津北区' }
]
}
]
让我猜猜你是怎么想的
通过filter过滤出name == 天津 || name == 河北 ?
如果你也是这么想的,如果让你获取沧州下一级的信息怎么处理呢?
在树形结构上,我们操作元素是很复杂的,需要很多循环和递归,每次寻找都要遍历,子级查找父级也很繁琐,怎么才能一劳永逸呢?是否可以转换一次之后,以后调用转换后的数据结果?
给你一个数据最好获取属性的是什么数据结构? 对象{}
如何把树形结构转换为对象?
我们要注意以下三点
- 保持树形原有的数据结构
- 属性唯一性
- 父子级关联性
找到数据结构中唯一值,
value值是唯一的,可以使用value作为属性的[key],在子集添加属性parentIds来存储父级所有的key做子父级的关联属性,转换后的数据结构如下:
{
"1":{
"value":"1",
"name":"河北",
"children":[
{
"value":"10",
"name":"沧州",
"children":[
{ "value":"101", "name":"沧县" },
{ "value":"102", "name":"泊头" },
{ "value":"103", "name":"衡水" }
]
}
],
"parentIds":[ ]
},
"2":{
"value":"2",
"name":"北京",
"children":[
{ "value":"20", "name":"朝阳区" },
{ "value":"21", "name":"西城区" },
{ "value":"22", "name":"东城区" }
],
"parentIds":[ ]
},
"3":{
"value":"3",
"name":"天津",
"children":[
{ "value":"31", "name":"武清区" },
{ "value":"32", "name":"津南区" },
{ "value":"33", "name":"津北区" }
],
"parentIds":[ ]
},
"10":{
"value":"10",
"name":"沧州",
"children":[
{ "value":"101", "name":"沧县" },
{ "value":"102", "name":"泊头" },
{ "value":"103", "name":"衡水" }
],
"parentIds":[ "1" ]
},
"20":{
"value":"20",
"name":"朝阳区",
"parentIds":[ "2" ]
},
"21":{
"value":"21",
"name":"西城区",
"parentIds":[ "2" ]
},
"22":{
"value":"21",
"name":"东城区",
"parentIds":[ "2" ]
},
"31":{
"value":"31",
"name":"武清区",
"parentIds":[ "3" ]
},
"32":{
"value":"32",
"name":"津南区",
"parentIds":[ "3" ]
},
"33":{
"value":"33",
"name":"津北区",
"parentIds":[ "3" ]
},
"101":{
"value":"101",
"name":"沧县",
"parentIds":[ "1", "10" ]
},
"102":{
"value":"102",
"name":"泊头",
"parentIds"::[ "1", "10" ]
},
"103":{
"value":"103",
"name":"衡水",
"parentIds":[ "1", "10" ]
}
}
转换tree为obj
obj的key为value,parentIds存储所有父类的value,无父类则返回[]
/**
* 转换tree为obj,obj的key为value,parentIds存储所有父类的value,无父类则返回[]
* @param {*} tree 树形节点
* @param {*} parentIds 父级ID
* @param {*} obj 上一次处理后obj的结果
*/
function getObjByTree(tree, parentIds = [], obj = {}) {
//遍历树形下的元素结构
for (let i = 0; i < tree.length; i++) {
const item = tree[i];
obj[item.value] = {//设置转换后的属性和值
...item,//保存当前数据结构
parentIds: parentIds //存储所有的父级ID
}
if (item.children) {//如果存在子集则递归执行以上逻辑
getObjByTree(item.children, [...parentIds, item.value], obj)
}
}
return obj
}
实际案例
背景介绍
树形层级不确定,实现树形数据多选查找下级
效果展示
代码实现
状态存储初始化
- 初始值设定
this.state = {
treeMapDistance: {},
selectKeyObj: { 0: { list: [], values: [] } }
};
- 获取转换后的树形结构,并设置初始值
componentDidMount = () => {
let treeMapDistance = getObjByTree(tree)
this.setState({
treeMapDistance: treeMapDistance,
selectKeyObj: { 0: { list: tree, values: [] } }
})
}
页面结构
使用 Object.keys(selectKeyObj) 实现多层渲染
render() {
const { selectKeyObj } = this.state
return (
<div>
{(Object.keys(selectKeyObj).map((selectKey, index) => <Select
mode="multiple"
value={selectKeyObj[selectKey].values}
onChange={(values) => this.setSelectKey(values, index)}
key={index}
className='multiple-select-item'>
{selectKeyObj[selectKey].list.map(one => <Option
value={one.value}
key={one.value}> {one.name} </Option>)}
</Select>))}
</div>
);
设置当前选中项和下级列表数据 setSelectKey
- 存储当前选中值,并获取下级元素,有责设置
list数据,无责设置[]
setSelectKey = (values, key) => {
const { selectKeyObj } = this.state
let selectKeyNew = { ...selectKeyObj }
//补充当次点击数据
selectKeyNew[key] = {
list: selectKeyObj[key].list,
values: values
}
//添加下一级list展示项
const nextList = this.getList(values)
if (nextList.length > 0) {
selectKeyNew[key + 1] = {
list: nextList,
values: []
}
}
this.setState({
selectKeyObj: selectKeyNew,
})
}
- 获取下级元素方法
/**
* 根据values获取下一级的数组
* @param {*} values 父级元素选中的值
*/
getList = (values) => {
const { treeMapDistance } = this.state
return values.map(item => treeMapDistance[item].children || []).flat()
}
- 效果展示
跳级处理
- 修改父级,清空子集选中项
setSelectKey = (values, key) => {
const { selectKeyObj } = this.state
let selectKeyNew = { ...selectKeyObj }
//+++++添加跳级处理开始
let list = Object.keys(selectKeyObj)//获取当前数组
let children = list.splice(key, list.length)//获取下级数据
for (let i = 0; i < children.length; i++) {//清空下级数据
const key = children[i];
delete selectKeyNew[key]
}
//+++++添加跳级处理结束
//补充当次点击数据
selectKeyNew[key] = {
list: selectKeyObj[key].list,
values: values
}
....
- 效果如下