前言:筆者在前端開發(fā)過程中遇到關(guān)于樹的問題,把a(bǔ)ntd官方的Tree組件直接拿下來用了,剛開始其他方法都還好,當(dāng)需求中寫個(gè)節(jié)點(diǎn)的刪除功能的時(shí)候,因?yàn)樗惴üΦ撞缓?,?dǎo)致此功能實(shí)現(xiàn)不了,所以有了此文
背景
前端項(xiàng)目是react+umi+antd,這個(gè)用過的都知道好,不做贅述,筆者先找到了這個(gè)antd官方提供的Tree組件
export default class MyTree extends React.Component {
state = {
treeData: [
{
key:'1',
title:"第一部分",
children:[
{key:'1-1',title:"一、二級(jí)標(biāo)題",},
{key:'1-2',title:"二、二級(jí)標(biāo)題",},
{key:'1-3',title:"三、二級(jí)標(biāo)題",},
{key:'1-4',title:"四、二級(jí)標(biāo)題",}
]
},
{
key:'2',
title:"第二部分",
children:[
{
key:'2-1',
title:"一、二級(jí)標(biāo)題",
children:[
{key:'2-1-1',title:"(一)三級(jí)標(biāo)題",},
{key:'2-1-2',title:"(二)三級(jí)標(biāo)題",},
{key:'2-1-3',title:"(三)三級(jí)標(biāo)題",},
]
},
{
key:'2-2',
title:"二、二級(jí)標(biāo)題",
children:[
{key:'2-2-1',title:"(一)三級(jí)標(biāo)題",},
{key:'2-2-2',title:"(二)三級(jí)標(biāo)題",}
]
},
{
key:'2-3',
title:"三、二級(jí)標(biāo)題",
children:[
{key:'2-3-1',title:"(一)三級(jí)標(biāo)題",},
{key:'2-3-2',title:"(二)三級(jí)標(biāo)題",},
]
},
{
key:'2-4',
title:"四、二級(jí)標(biāo)題",
children:[
{key:'2-4-1',title:"(一)三級(jí)標(biāo)題",},
{key:'2-4-2',title:"(二)三級(jí)標(biāo)題",},
]
},
{
key:'2-5',
title:"五、二級(jí)標(biāo)題",
children:[
{key:'2-5-1',title:"(一)三級(jí)標(biāo)題",},
{key:'2-5-2',title:"(二)三級(jí)標(biāo)題",},
]
},
]
},
{
key:'3',
title:"第三部分",
children:[
{
key:'3-1',
title:"一、二級(jí)標(biāo)題",
children:[
{key:'3-1-1',title:"(一)綜合醫(yī)院",},
{key:'3-1-2',title:"(二)重點(diǎn)??漆t(yī)院",},
]
},
{
key:'3-2',
title:"二、二級(jí)標(biāo)題",
children:[
{key:'3-2-1',title:"(一)三級(jí)標(biāo)題",},
{key:'3-2-2',title:"(二)三級(jí)標(biāo)題",},
]
},
{
key:'3-3',
title:"三、二級(jí)標(biāo)題",
children:[
{key:'3-3-1',title:"(一)三級(jí)標(biāo)題",},
{key:'3-3-2',title:"(二)三級(jí)標(biāo)題",},
]
},
{
key:'3-4',
title:"四、二級(jí)標(biāo)題",
children:[
{key:'3-4-1',title:"(一)三級(jí)標(biāo)題",},
{key:'3-4-2',title:"(二)三級(jí)標(biāo)題",},
]
},
]
}
]
}
renderTreeNodes = data => data.map((item) => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode {...item} dataRef={item} />;
})
render() {
return (
<Tree>
{this.renderTreeNodes(this.state.treeData)}
</Tree>
);
}
}
通過這個(gè)結(jié)構(gòu),render出來的結(jié)果,和https://ant.design/components/tree-cn/中的效果

尋找刪除之道
這個(gè)時(shí)候又行業(yè)大佬提供了以下思路
1:遞歸去找到然后刪除
2:把key值不等于這個(gè)得,重新放入一個(gè)新數(shù)組,最后重新設(shè)置state
3:把key值直接設(shè)置為className,找dom數(shù)然后刪除
其實(shí)三種方法都可以進(jìn)行探索,網(wǎng)上找了N多資料,都不得解,最終晚上靈光一閃,得出以下思路:
如果把data數(shù)組,都放在最頂層,取消children數(shù)組,然后給每個(gè)都加上一個(gè)parentId,
1:這樣滿足了render的時(shí)候可以進(jìn)行遞歸,
2:要?jiǎng)h除的時(shí)候,或者更改的時(shí)候,遍歷data,就能找打key,進(jìn)行刪除,當(dāng)然,如果刪除的是父級(jí),同時(shí)也可以刪除所有子級(jí)節(jié)點(diǎn)
以下是代碼
export default class MyTree extends React.Component {
state = {
data:[
{key:'1',title:'第一部分',parentId:'-1'},
{key:'1-1',title:'一、二級(jí)標(biāo)題',parentId:'1'},
{key:'1-2',title:'二、二級(jí)標(biāo)題',parentId:'1'},
{key:'1-3',title:'三、二級(jí)標(biāo)題',parentId:'1'},
{key:'1-4',title:'四、二級(jí)標(biāo)題',parentId:'1'},
{key:'2',title:'第二部分',parentId:'-1'},
{key:'2-1',title:'一、二級(jí)標(biāo)題',parentId:'2'},
{key:'2-1-1',title:'(一)三級(jí)標(biāo)題',parentId:'2-1'},
{key:'2-1-2',title:'(二)三級(jí)標(biāo)題',parentId:'2-1'},
{key:'2-1-3',title:'(三)三級(jí)標(biāo)題',parentId:'2-1'},
{key:'2-2',title:'二、二級(jí)標(biāo)題',parentId:'2'},
{key:'2-2-1',title:'(一)三級(jí)標(biāo)題',parentId:'2-2'},
{key:'2-2-2',title:'(二)三級(jí)標(biāo)題',parentId:'2-2'},
{key:'2-3',title:'三、二級(jí)標(biāo)題',parentId:'2'},
{key:'2-3-1',title:'(一)三級(jí)標(biāo)題',parentId:'2-3'},
{key:'2-3-2',title:'(二)三級(jí)標(biāo)題',parentId:'2-3'},
{key:'2-4',title:'四、二級(jí)標(biāo)題',parentId:'2'},
{key:'2-4-1',title:'(一)三級(jí)標(biāo)題',parentId:'2-4'},
{key:'2-4-2',title:'(二)三級(jí)標(biāo)題',parentId:'2-4'},
{key:'2-5',title:'五、二級(jí)標(biāo)題',parentId:'2'},
{key:'2-5-1',title:'(一)三級(jí)標(biāo)題',parentId:'2-5'},
{key:'2-5-2',title:'(二)三級(jí)標(biāo)題',parentId:'2-5'},
{key:'3',title:'第三部分',parentId:'-1'},
{key:'3-1',title:'一、二級(jí)標(biāo)題',parentId:'3'},
{key:'3-1-1',title:'(一)三級(jí)標(biāo)題',parentId:'3-1'},
{key:'3-1-2',title:'(二)三級(jí)標(biāo)題',parentId:'3-1'},
{key:'3-2',title:'二、二級(jí)標(biāo)題',parentId:'3'},
{key:'3-2-1',title:'(一)三級(jí)標(biāo)題',parentId:'3-2'},
{key:'3-2-2',title:'(二)三級(jí)標(biāo)題',parentId:'3-2'},
{key:'3-3',title:'三、二級(jí)標(biāo)題',parentId:'3'},
{key:'3-3-1',title:'(一)三級(jí)標(biāo)題',parentId:'3-3'},
{key:'3-3-2',title:'(二)三級(jí)標(biāo)題',parentId:'3-3'},
{key:'3-4',title:'四、二級(jí)標(biāo)題',parentId:'3'},
{key:'3-4-1',title:'(一)三級(jí)標(biāo)題',parentId:'3-4'},
{key:'3-4-2',title:'(二)三級(jí)標(biāo)題',parentId:'3-4'},
],
}
// 根據(jù)父級(jí)找到所有子級(jí)節(jié)點(diǎn)
getByParentId(parentId){
return this.state.data.filter(item => {
return item.parentId === parentId;
})
}
renderTreeNode = (parentId) => {
// 先找到子級(jí)節(jié)點(diǎn)
var tmp = this.getByParentId(parentId);
if(tmp.length > 0){
// 遍歷鋪頁(yè)面,如果數(shù)組長(zhǎng)度不為0則證明子級(jí)不為空
return tmp.map(item =>{
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNode(item.key)}
</TreeNode>
);
})
}
}
render() {
return (
<Tree>
{/*先找到所有parentId為-1的頂級(jí)節(jié)點(diǎn)*/}
{this.renderTreeNode("-1")}
</Tree>
);
}
}

接下來就好辦了,刪除方法和右鍵響應(yīng)方法
//簡(jiǎn)單處理,右鍵刪除
handleRightClick = (e) =>{
let key = e.node.props.dataRef.key;
this.handleDelete(key)
}
//根據(jù)key刪除節(jié)點(diǎn)
handleDelete = (key) => {
let {data} = this.state;
data.splice(data.findIndex(item => item.key === key),1)
this.setState(data);
}
Tree綁定右鍵處理事件
<Tree onRightClick={this.handleRightClick}>
{/*先找到所有parentId為-1的頂級(jí)節(jié)點(diǎn)*/}
{this.renderTreeNode("-1")}
</Tree>
效果:

同理,要對(duì)節(jié)點(diǎn)進(jìn)行其他遍歷操作就更方便,不會(huì)像第一次那樣做各種遞歸處理,給不同節(jié)點(diǎn)增加不同樣式,修改內(nèi)容等等,方便很多
鑒于樓下有個(gè)小伙伴,提問如何更換位置,今日有空,也實(shí)現(xiàn)出來,大家多多指導(dǎo)
class MyTree extends React.Component {
state = {
currentNode: null,
data:[
{key:'1',title:'第一部分',parentId:'-1'},
{key:'2',title:'第二部分',parentId:'-1'},
{key:'3',title:'第三部分',parentId:'-1'},
{key:'1-1',title:'一、二級(jí)標(biāo)題',parentId:'1'},
{key:'1-2',title:'二、二級(jí)標(biāo)題',parentId:'1'},
{key:'1-3',title:'三、二級(jí)標(biāo)題',parentId:'1'},
{key:'1-4',title:'四、二級(jí)標(biāo)題',parentId:'1'},
{key:'2-1',title:'一、二級(jí)標(biāo)題',parentId:'2'},
{key:'2-2',title:'二、二級(jí)標(biāo)題',parentId:'2'},
{key:'2-3',title:'三、二級(jí)標(biāo)題',parentId:'2'},
{key:'2-4',title:'四、二級(jí)標(biāo)題',parentId:'2'},
{key:'2-5',title:'五、二級(jí)標(biāo)題',parentId:'2'},
{key:'3-1',title:'一、二級(jí)標(biāo)題',parentId:'3'},
{key:'3-2',title:'二、二級(jí)標(biāo)題',parentId:'3'},
{key:'3-3',title:'三、二級(jí)標(biāo)題',parentId:'3'},
{key:'3-4',title:'四、二級(jí)標(biāo)題',parentId:'3'},
{key:'2-1-1',title:'(一)三級(jí)標(biāo)題',parentId:'2-1'},
{key:'2-1-2',title:'(二)三級(jí)標(biāo)題',parentId:'2-1'},
{key:'2-1-3',title:'(三)三級(jí)標(biāo)題',parentId:'2-1'},
{key:'2-2-1',title:'(一)三級(jí)標(biāo)題',parentId:'2-2'},
{key:'2-2-2',title:'(二)三級(jí)標(biāo)題',parentId:'2-2'},
{key:'2-3-1',title:'(一)三級(jí)標(biāo)題',parentId:'2-3'},
{key:'2-3-2',title:'(二)三級(jí)標(biāo)題',parentId:'2-3'},
{key:'2-4-1',title:'(一)三級(jí)標(biāo)題',parentId:'2-4'},
{key:'2-4-2',title:'(二)三級(jí)標(biāo)題',parentId:'2-4'},
{key:'2-5-1',title:'(一)三級(jí)標(biāo)題',parentId:'2-5'},
{key:'2-5-2',title:'(二)三級(jí)標(biāo)題',parentId:'2-5'},
{key:'3-1-1',title:'(一)三級(jí)標(biāo)題',parentId:'3-1'},
{key:'3-1-2',title:'(二)三級(jí)標(biāo)題',parentId:'3-1'},
{key:'3-2-1',title:'(一)三級(jí)標(biāo)題',parentId:'3-2'},
{key:'3-2-2',title:'(二)三級(jí)標(biāo)題',parentId:'3-2'},
{key:'3-3-1',title:'(一)三級(jí)標(biāo)題',parentId:'3-3'},
{key:'3-3-2',title:'(二)三級(jí)標(biāo)題',parentId:'3-3'},
{key:'3-4-1',title:'(一)三級(jí)標(biāo)題',parentId:'3-4'},
{key:'3-4-2',title:'(二)三級(jí)標(biāo)題',parentId:'3-4'},
],
}
// 根據(jù)父級(jí)找到所有子級(jí)節(jié)點(diǎn)
getByParentId(parentId){
return this.state.data.filter(item => {
return item.parentId === parentId;
})
}
renderTreeNode = (parentId) => {
// 先找到子級(jí)節(jié)點(diǎn)
let tmp = this.getByParentId(parentId);
if(tmp.length > 0){
// 遍歷鋪頁(yè)面,如果數(shù)組長(zhǎng)度不為0則證明子級(jí)不為空
return tmp.map(item =>{
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNode(item.key)}
</TreeNode>
);
})
}
}
handleRightClick = ({event,node}) =>{
let key = node.props.dataRef.key;
this.handleDelete(key)
}
/**
* 上下移動(dòng)
*/
handleMoveOn=()=> {
let data = this.state.data;
for(let i = 0; i< data.length; i++){
if(data[i].key == this.state.currentNode && i >= 1){
let tmp = data[i];
data[i] = data[i-1]
data[i-1] = tmp;
}
}
this.setState(data)
}
handleClick=(e)=>{
console.log(1)
this.setState({currentNode:e})
}
//根據(jù)key刪除節(jié)點(diǎn)
handleDelete = (key) => {
let {data} = this.state;
data.splice(data.findIndex(item => item.key === key),1)
this.setState(data);
}
render() {
return (
<div>
<div className={styles.btn}>
<Button onClick={this.handleMoveOn} disabled={this.state.currentNode==null}>上移</Button>
</div>
<Tree onRightClick={this.handleRightClick} className={styles.normal} onSelect={this.handleClick}>
{/*先找到所有parentId為-1的頂級(jí)節(jié)點(diǎn)*/}
{this.renderTreeNode("-1")}
</Tree>
</div>
);
}
}

鳴謝:感謝antd、umi 提供優(yōu)秀的前端框架
版權(quán)聲明:本文原創(chuàng),轉(zhuǎn)載請(qǐng)聲明出處,謝謝