前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >树形结构踩坑记

树形结构踩坑记

作者头像
一粒小麦
发布2019-07-18 17:46:23
1.3K0
发布2019-07-18 17:46:23
举报
文章被收录于专栏:一Li小麦一Li小麦

树形结构数据的查询、渲染和删除是一类常见的问题。

初始问题:如何从树形结构中检索数据

两个月前有个初级前端卡在这个需求。现有数据结构如下:

代码语言:javascript
复制
const data = [{
    id: 9,
    title: 'Node2',
}, {
    id: 1,
    title: 'Node1',
    children: [{
        id: 2,
        title: 'Child Node1',
    }, {
        id: 3,
        title: 'Child Node2',
    }, {
        id: 4,
        title: 'Child Node2',
        children: [{
            id: 5,
            title: 'Child Node1',
        }, {
            id: 6,
            title: 'Child Node2',
        }, {
            id: 7,
            title: 'Child Node7',
        },]
    },]
}, {
    id: 8,
    title: 'Node2'
}]

如何查询指定id下的data?

我给出递归的方案:

代码语言:javascript
复制
const findOne = (_data, id) => {
    let i = 0;
    let result = null;
    while (i < _data.length) {
        if (_data[i].id == id) {
            result = _data[i];
            break;
        } else if (_data[i].children&& findOne(_data[i].children, id)) {
            return findOne(_data[i].children, id);
        } else {
            i++;
        }
    }
    return result
}

//测试
console.log(findOne(data, 5))

函数将返回指定id下的所有数据。

在react中如何渲染树结构

项目以 antD为例:

这个数据结构,除了章节节点之外还有习题,最初后端给出的是两个表联查得出的数据结构:

习题放在了和children同层级的resource中。children对应的id为value,resource对应的为resourceid。

而标准的渲染,是必须把习题也放入到children中的。

代码语言:javascript
复制
// 渲染树形结构
  renderTree(arr, parentNode) {
    let cHtml = <div></div>;
    let _this = this;
    arr = arr.map((x, i) => {
      if (x.children) {
        x.children = x.children.concat(x.rescourse)
        // console.log(x)
        return <TreeNode 
        key={x.value} 
        title={<span>{x.label}&nbsp;&nbsp;<a style={{
          color: 'rgb(255,150,50)'
        }} href="javascript:;" onClick={(e) => {
          e.stopPropagation();
          this.nodeoOnEdit(x)
        }}><Icon type="edit" /></a>
          &nbsp;&nbsp;<a style={{
            color: 'rgb(241,102,82)'
          }} href="javascript:;" onClick={(e) => {
            e.stopPropagation();
            this.nodeoOnDel(x)
          }}><Icon type="delete" /></a>
        </span>} >{_this.renderTree(x.children, x.value)}</TreeNode>
      } else {
        return <TreeNode
          key={arr[i].value}
          // key={parentNode} 
          title={<span>{arr[i].title}&nbsp;&nbsp;<a style={{
            color: 'rgb(241,102,82)'
          }} href="javascript:;" onClick={(e) => {
            e.stopPropagation();
            this.unitDataOnDel(x, parentNode)
          }}><Icon type="delete" /></a></span>}
          isLeaf />
      }
    })

    return arr;
  }

解决要点:

  • 每次做循环的时候,把resource衔接children上去!
  • 如果没有chilren,进入渲染流程。否则进入递归迭代。

如果我把请求后获取的数据作为 renderTree的参数,势必造成state的改变。

这里需要用到一个hack,就是每次渲染都用深度拷贝的数据。深度拷贝的方法有很多种,也被用来出题。而最简单的:

代码语言:javascript
复制
let new_obj=JSON.parse(JSON.stringify(obj))

如果不考虑性能,这个操作也是逆天的。

删除树形结构

按理来说,后端操作这个是最快的。前端只需要指定一个id即可。

结果后端设计结构时把他们设计为两个表了。删除变得异常复杂。因此需要前端告诉他树形节点的所有id。

因此需要更好的完善 renderTree

这里就用到了 findOne方法。

核心方法如下:

代码语言:javascript
复制
getValue(obj, new_arr) {
    try {
      new_arr.push(obj.value)
    } catch (error) {
      console.log(new_arr)
    }
    if (obj.children) {
      for (let i = 0; i < obj.children.length; i++) {
        this.getValue(obj.children[i], new_arr)
      }
      return new_arr
    }
  }

与之前的相比,这个方法多了一个初始值为 []的参数 new_arr.作为递归的累加器(数组)。每次循环递归渲染,都会记录所有的节点id。

在删除方法中这么写:

代码语言:javascript
复制
let aaa = this.findOne(this.state.courseData, x.value);
aaa = this.getValue(aaa, [])

那么就得到了一个id列表。发给后端,他就可以愉快的删除了。

小结与不足

所有一系列问题的核心在于,后端采用了两个表来设计。树的结构有可能拥有一样的value。这是比较蛋疼的事情。

那么留作思考的问题来了:

应如何组织数据结构,才能很快的实现value值的不冲突呢?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一Li小麦 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 初始问题:如何从树形结构中检索数据
  • 在react中如何渲染树结构
  • 删除树形结构
  • 小结与不足
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档