封装 Tree 遇到的问题和知识点
1. 怎么封装?
用数据自己去渲染组件
- {
- type:"rulename"
- children:[
- {
- type:"condition",
- children:[]},
- {
- type:"action",
- children:[]
- }
- ]
- }
- // 组件遇到 type 就知道自己用哪个组件!
2. 思路
TreeData--------> Tree------------>TreeNode
3.TreeNode 的设计
最原始的 TreeNode
- <div className="treenode">
- <div className="title">
- <Icon type="arrow-down" size='xs' />
- <span>
- {renderRoot} // 渲染 第一个节点
- </span>
- </div>
- <div className="context">
- {renderTreeNode} // 渲染留白区域
- </div>
- </div>
- .treenode{
- width:100%;
- height:100%;
- i{
- margin-right: 5px !important;
- line-height: inherit;
- }
- span{
- width:200px;
- line-height: 23px;
- }
- }
4.TreeNode 的递归
思路: 每一层只管自己的数据, 如自己当前一层的 type,key,value
- TreeData: {
- type: 'rulename',
- key: '0',
- value: '',
- children: [
- {
- type:'ruleConditions',
- key: '0-0',
- value: '条件'
- },
- {
- type: 'action',
- key: '0-1',
- value: '实例'
- }
- ]
- },
数据一进来, 只有 rulename, 需要一个专门根据 type 来渲染组件的渲染变量
- let renderComponent;
- switch(type){
- case "rulename":
- renderComponent = (<div>123</div>)
- break;
- default:
- renderComponent=null
- break;
- }
遇到的问题 一开始写的递归
- if(!!dataSource.children){
- dataSource.children.map(item=>{
- let {children,{...rest}}=item
- return <TreeNode dataSource={children} {...rest}/>
- })
- }
这样写会有一个问题 结合起来看
- let { dataSource } =this.props
- let {type,key,value}={...dataSource}; //children 递归回来, 已经没有 type,key,value 了
- let renderRoot;
- let renderTreeNode = [];
- if(!!dataSource.children){
- renderTreeNode=dataSource.children.map(item=>{
- let {children,{...rest}}=item
- //{...rest} 里面包含的是我这一层需要的 type,key,value
- return <TreeNode dataSource={children} {...rest}/>
- // 问题所在, children 递归回去, 没有我要的东西了
- })
- }
- switch(type){
- case "rulename":
- renderRoot = (<div>123</div>)
- break;
- default:
- renderRoot=null
- break;
- }
急需解决的问题:
1.children 递归回来, 没有 type,key,value 了, 全部留在递归前的 {...rest} 了, 怎么根据取出来去渲染呢?
2.renderRoot 和 renderTreeNode 不能同时被渲染出来, rulename 和 ruleConditions,action 没法共同渲染
3. 而且 children 里面还有其他字段需要传递给组件, 无法传出去给组件, 继续完成递归
解决思路:
1. 我把 type 和 children 单独拿出来, 其他参数我不管, 传进传出, 自己去传, 自己去解构
2. 渲染需要两个变量来自动完成, 一个变量渲染父节点, 一个变量渲染子节点
3. 其余的数据结构里面的字段, 全部传过去, 自己去解构
我需要
1. 一个专门负责递归的函数, 并能渲染子组件的变量
- (递归 children 的函数和渲染 children 的变量)
- let { dataSource } =this.props
- let {type,children,...rest}={...dataSource}; // 把 type 和 children 单独拿出来
- if(children){
- renderTreeNode=this.reduceChild(children,renderTreeNode)
- }
- /**
- * @func reduceChild
- * @desc 如果有 children, 进行 TreeNode 递归,
- * @param ChildData 传过来的 children 数据
- * @param renderTreeNode 专门用来渲染子节点的数组, reduceChild 要返回回去一个新的渲染数组
- */
- reduceChild(ChildData,renderTreeNode){
- return renderTreeNode=ChildData.map((item,index)=>{
- return <TreeNode dataSource={item} key={index}/>
- })
- }
遍历的时候需要个唯一的 key, 通过加 index 解决
遍历时报的错
2. 一个专门负责根据 type 渲染组件的函数
- let renderRoot;
- renderRoot=this.JudgeType(type,{...rest}) //TreeData 里的 rulename
- /**
- * @func JudgeType
- * @desc 根据数据结构里的 type 字段渲染不同的组件
- * @param type 数据结构里面的 type 字段
- * @param rest 数据结构里面除了 type 外的其他字段内容, 需要哪个字段, 就解构出来
- */
- JudgeType(type,rest){
- let renderComponent;
- let {value,key}={...rest}
- switch(type){
- case "rulename":
- renderComponent = (<InputInTree value={value} uid={key} onChange={this.onInputChange.bind(this)}/>)
- break;
- case "ruleConditions":
- renderComponent =(<Add value={value} isAddShow={true} uid={key} addItem={this.addCondition.bind(this)} key="0-0"/>)
- break;
- case "editCondition":
- renderComponent =(<TwoInputAndSelectInTree isDeleteShow={true} uid={key} deleteItem={this.deleteItemCondition.bind(this)}/>)
- break;
- case "action":
- renderComponent =(<Add value={value} isAddShow={true} uid={key} addItem={this.addCondition.bind(this)} key="0-1"/>)
- break;
- case "actionInstance":
- renderComponent =(<SelectInTree isAddShow={true} isDeleteShow={true}/>)
- break;
- case "config":
- renderComponent =(<SelectInTree isAddShow={true} isDeleteShow={true}/>)
- break;
- case "Type":
- renderComponent =(<SelectInTree isDeleteShow={true}/>)
- break;
- default:
- renderComponent=null
- break;
- }
- return renderComponent;
- }
完整代码段
- import React from 'react';
- import './TreeNode.sCSS'
- import '../EditComponent/index.scss'
- import { Icon} from "@alife/next";
- import InputInTree from "../EditComponent/InputInTree/InputInTree"
- import Add from '../EditComponent/Add/Add'
- import TwoInputAndSelectInTree from '../EditComponent/TwoInputAndSelectInTree/TwoInputAndSelectInTree'
- import SelectInTree from "../EditComponent/SelectInTree/SelectInTree"
- const deepClone=obj=>{
- return JSON.parse(JSON.stringify(obj))
- }
- class TreeNode extends React.Component {
- addCondition(addConditionkey){
- console.log(addConditionkey)
- }
- deleteItemCondition(deleteConditionKey){
- console.log(deleteConditionKey)
- }
- onInputChange(key,value){
- console.log(key,value)
- }
- /**
- * @func JudgeType
- * @desc 根据数据结构里的 type 字段渲染不同的组件
- * @param type 数据结构里面的 type 字段
- * @param rest 数据结构里面除了 type 外的其他字段内容, 需要哪个字段, 就解构出来
- */
- JudgeType(type,rest){
- let renderComponent;
- let {value,key}={...rest}
- switch(type){
- case "rulename":
- renderComponent = (<InputInTree value={value} uid={key} onChange={this.onInputChange.bind(this)}/>)
- break;
- case "ruleConditions":
- renderComponent =(<Add value={value} isAddShow={true} uid={key} addItem={this.addCondition.bind(this)} key="0-0"/>)
- break;
- case "editCondition":
- renderComponent =(<TwoInputAndSelectInTree isDeleteShow={true} uid={key} deleteItem={this.deleteItemCondition.bind(this)}/>)
- break;
- case "action":
- renderComponent =(<Add value={value} isAddShow={true} uid={key} addItem={this.addCondition.bind(this)} key="0-1"/>)
- break;
- case "actionInstance":
- renderComponent =(<SelectInTree isAddShow={true} isDeleteShow={true}/>)
- break;
- case "config":
- renderComponent =(<SelectInTree isAddShow={true} isDeleteShow={true}/>)
- break;
- case "Type":
- renderComponent =(<SelectInTree isDeleteShow={true}/>)
- break;
- default:
- renderComponent=null
- break;
- }
- return renderComponent;
- }
- /**
- * @func reduceChild
- * @desc 如果有 children, 进行 TreeNode 递归,
- * @param ChildData 传过来的 children 数据
- * @param renderTreeNode 专门用来渲染子节点的数组, reduceChild 要返回回去一个新的渲染数组
- */
- reduceChild(ChildData,renderTreeNode){
- return renderTreeNode=ChildData.map((item,index)=>{
- return <TreeNode dataSource={item} key={index}/>
- })
- }
- /**
- * @func reduceChild
- * @desc TreeData 一进来是一个对象,
- type 要用来判断显示怎么样的组件,
- children 要用来递归
- {...rest} 里面包含了除了 type 和 children 的其他字段传给组件,
- 比如: 需要的 value 和 key, 传给自定义组件去使用
- * @param renderRoot // 专门负责渲染最上层的 rulename
- * @param renderTreeNode // 专门负责渲染子节点的数组
- */
- render() {
- let { dataSource } =this.props
- let {type,children,...rest}={...dataSource}; // 把 type 和 children 单独拿出来
- let renderRoot;
- let renderTreeNode = [];
- if(children){
- renderTreeNode=this.reduceChild(children,renderTreeNode)
- }
- renderRoot=this.JudgeType(type,{...rest})//TreeData 里的 rulename
- return (
- <div className="treenode">
- <div className="title">
- <Icon type="arrow-down" size='xs' />
- <span>
- {renderRoot}
- </span>
- </div>
- <div className="context">
- {renderTreeNode}
- </div>
- </div>
- )
- }
- }
- export default TreeNode;
来源: http://www.jianshu.com/p/de35e082f323