如何用react封裝一款Tree 樹形元件

語言: CN / TW / HK

theme: channing-cyan highlight: night-owl


這是我參與11月更文挑戰的第11天,活動詳情檢視:[2021最後一次更文挑戰](https://juejin.cn/post/7023643374569816095/ "https://juejin.cn/post/7023643374569816095/") > TIP 👉 **嗚呼!楚雖三戶能亡秦,豈有堂堂中國空無人!____陸游《金錯刀行》**

前言

Web Component是前端界一直非常熱衷的一個領域,用第三方元件化的框架去實現的話,你需要依賴框架本身很多東西,很多時候我們只是簡單的幾個元件,不是很大,也不是很多,所以為了保證元件的`輕量,簡單`,其實這個時候我們並不想採用第三方的框架。 # Tree 樹形控制元件 ### import ##### 建議用tree2資料,通用後端給的json資料 ```js import Tree from '@/components/Tree/Tree2'; ``` ### Props ##### 1. treeData * 型別:array (必填) * 預設值:[] * 說明:樹形資料 ##### 2. iconFolder * 型別:boolean (必填) * 預設值:true / false * 說明:是否有資料夾的折起標誌 ##### 3. iconStyle * 型別:boolean (必填) * 預設值:true / false * 說明:是否有加減的折起標誌 ##### 4. canSelected * 型別:boolean (必填) * 預設值:true / false * 說明:是否可勾選 ##### 5. clickNodeGetInfo * 型別:func * 預設值:無 * 說明:點選觸發回撥函式,入參如: * {obj} node 觸發樹的某個節點 ##### 6. parentCodeName * 型別:string (必填) * 預設值:'parentCode' * 說明:參照後端資料查詢父節點值欄位,不固定值 ##### 7. codeName * 型別:string (必填) * 預設值:'code' * 說明:參照後端資料查詢子節點值欄位,不固定值 實現Tree.js ```js import React from 'react'; import './Tree.scss'; /** * Tree 元件所傳屬性描述 * treeList 樹的所有資料 arr * onClick 點選樹的執行方法 函式 * loadDataAction 動態載入資料 函式 */ class Tree extends React.Component { constructor(props){ super(props); this.handleClick = this.handleClick.bind(this); this.state = { node: props.treeList, } } handleClick(e,item) { let Oi = e.target.parentNode.childNodes[0] ; // Oi.style.transform = Oi.style.transform == "rotate(-90deg)" ? "rotate(0deg)" : "rotate(-90deg)"; this.digui(this.state.node,item); this.props.onClick && this.props.onClick(item); this.props.loadDataAction && this.props.loadDataAction(item); } checkAction(item){ item.checked = !item.checked; this.setState({ node : this.state.node }); // console.log(item); } digui(node,item){ let newNode = node; for(let i=0; i {item.title} ) }else{ return ( {this.handleClick(e,item)}}>{item.title} ) } } tree(child){ let treeItem; // 如果有子元素 if(child){ // 子元素是陣列的形式,把所有的子元素迴圈出來 treeItem = child.map((item, key) => { // 同理,設定樣式 let itemStyle = { paddingLeft: 20*parseInt(item.level.slice(5)-1)+'px', position:'relative' }; let wrapperStyle = { left: 5*parseInt(item.level.slice(5)-1)+'px', }; // 同理,設定➡️ let iconChevron; if(item.child){ if(item.open){ iconChevron = 'icon icon-xiangxiazuocedaohang'; }else{ iconChevron = 'icon icon-xiangyouzuocedaohang'; } }else{ iconChevron = 'icon tree-icon'; } return (
  • { this.props.loadDataAction ? ( (!(item.child && item.child.length)) ? {this.handleClick(e,item)}}> : ( this.props.notice ?
    : {this.handleClick(e,item)}}> ) ): {this.handleClick(e,item)}}> } { this.props.type == 'check' && } {this.itemTitle(item)}
  • {/* 如果當前子元素還有子元素,就遞迴使用tree方法,把當前子元素的子元素渲染出來 */} {item.open && this.tree(item.child)}
) }) } return treeItem; } render() { return (
{/*{this.tree(this.state.node)}*/} {this.tree(this.props.treeList)}
); } } export default Tree; ``` 樣式這塊就先不放了 「歡迎在評論區討論」 #### 希望看完的朋友可以給個贊,鼓勵一下