前言
最近在用Ant Design寫一個後臺,遇到的需求就是實現一個可動態增減和編輯子節點的Tree。GitHub上看了一圈,沒好用和合適的。索性就基於Ant Design中的Tree元件寫一個。實現的效果如下:
- 可以增加子節點
- 可以刪除子節點
- 可以編輯子節點資訊
- 可以取消編輯資訊
具體的效果圖如下:
主要的就是藉助 TreeNode
的 title
屬性,它的型別是string|ReactNode
。
正文
經過分析,一個節點的資料結構應該是
{
value: 'Root', // 顯示的資訊
defaultValue: 'Root', // 當某一節點進入編輯狀態,然後點選close按鈕,節點的資訊應該恢復原始狀態,
key: '0-1', // 節點的Key,全域性唯一
parentKey: '0', // 父節點的Key
isEditable: false // 是否處於可編輯狀態
children:[] // 子節點
}
複製程式碼
通過資料結構組裝TreeNode
的程式碼如下:
state = {
data: [
{
value: 'Root',
defaultValue: 'Root',
key: '0-1',
parentKey: '0',
isEditable: false
}
]
};
renderTreeNodes = data => data.map((item) => {
if (item.isEditable) { // 編輯狀態下
item.title = (
<div>
<input value={item.value}
onChange={(e) => this.onChange(e, item.key)}/>
<Icon type='close' style={{marginLeft:10}} onClick={() => this.onClose(item.key, item.defaultValue)}/>
<Icon type='check' style={{marginLeft:10}} onClick={() => this.onSave(item.key)}/>
</div>
);
} else {
item.title = (
<div>
<span>
{item.value}
</span>
<Icon style={{ marginLeft: 10 }} type='edit' onClick={() => this.onEdit(item.key)} />
<Icon style={{ marginLeft: 10 }} type='plus' onClick={() => this.onAdd(item.key)} />
{item.parentKey === '0' ? null : (<Icon style={{ marginLeft: 10 }} type='minus' onClick={() => this.onDelete(item.key)} />)} // 根節點沒有刪除按鈕
</div>
)
}
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode {...item}/>;
})
...
// 渲染介面
render() {
return (
<div>
<Tree>
{this.renderTreeNodes(this.state.data)}
</Tree>
</div>
)
}
複製程式碼
之後所有的增刪修改等都是修改this.state.data
這個陣列中的資料,具體的看下程式碼就成,很簡單。
需要注意的一點就是,每次更新過底層資料,都需要呼叫 this.forceUpdate()
這個方法,否則介面不會更新。
最後優化這個元件的時候,遇到一個比較坑的。本來想是當在某節點上增加子節點時,父節點自動展開,程式碼邏輯上沒有問題,但是必須手動執行過一次展開或者搜尋的操作,所寫的邏輯才能生效。後來沒辦法,只能在生命週期函式中DOM載入完畢後主動觸發下:
componentDidMount() {
this.onExpand([]); // 手動觸發,否則會遇到第一次新增子節點不展開的Bug
}
複製程式碼
程式碼放在GitHub上了,地址是 react-editable-tree,歡迎有同樣需要的小夥伴參考,star
和fork
也是極好的。