如何用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; ``` 樣式這塊就先不放了 「歡迎在評論區討論」 #### 希望看完的朋友可以給個贊,鼓勵一下