关于封装Antd的Tree组件,你也许不知道的事

9,570 阅读2分钟

一般的树形组件,也许会带上例如编辑、删除等自定义功能。例如下图:

AntdTree并不像elementUITree直接提供render-content来自定义节点内容的功能。而是只能给title传入自定义的ReactNode来渲染每个节点的内容。

那么代码也许会变成这样:

                <Tree
                    showLine
                >
                    <TreeNode title={<div>
                            	...节点名
                                <span>编辑</span>
                                <span>删除</span>
                            </div>} key="0-0">
                        <TreeNode title={<div>
                            	...节点名
                                <span>编辑</span>
                                <span>删除</span>
                            </div>} key="0-0-0">
                            <TreeNode title={<div>
                            	...节点名
                                <span>编辑</span>
                                <span>删除</span>
                            </div>} key="0-0-0-0" />
                            <TreeNode title="leaf" key="0-0-0-1" />
                        </TreeNode>
                    </TreeNode>
                </Tree>

也许你会说,title部分可以抽离,封装成一个组件。没错,当抽离了title部分的代码为一个组件时,我想到了,能否将整个TreeNode替换成我自己的TreeNode,类似这样:

                <Tree
                    showLine
                >
                    <TreeN title="parent 1" key="0-0">
                        <TreeN title="parent 1-0" key="0-0-0">
                            <TreeN title='leaf' key="0-0-0-0" />
                            <TreeN title="leaf" key="0-0-0-1" />
                        </Tree>
                    </TreeN>
                </Tree>

TreeN函数内部:

    ...//省略部分代码
    const {
        title,
        ...rest
    } = props;

    return (
        <TreeNode 
            title={
                <div className={treeNode}>
                    <span>{title}</span>
                    <span className={deleteBtn}>删除</span>
                    <span className={edit}>编辑</span>
                </div>
            }
            {...rest}
        />
    );

然而此时,控制台报了一个错:

Tree only accept TreeNode as children.
// Tree组件只接收TreeNode组件作为子组件

Antd的所有组件基本都是基于rc-xxxTree则是基于rc-tree
google一波,发现也有人有类似的情况,即使并未找到解决方案:

Tree 组件 warning 异常
递归实现 深层 Tree

查看编译后的代码,有这么一个函数:

function warnOnlyTreeNode() {
  if (onlyTreeNodeWarned) return;
  onlyTreeNodeWarned = true;
  (0, _warning.default)(false, 'Tree only accept TreeNode as children.');
}

那么rc-tree是如何做子组件类型校验的呢?组件只不过是一个函数,于是我查看了TreeNode.prototype发现,其构造函数中有这么一个属性:isTreeNode,值为1,这下就简单明了了,看看源码。

function isTreeNode(node) {
  return node && node.type && node.type.isTreeNode;
}

function getNodeChildren(children) {
  return (0, _toArray.default)(children).filter(isTreeNode);
}

rc-tree把子组件当中isTreeNode不为真的组件全部过滤掉了,所以我们直接加上这么个属性并赋值为1,就可以让rc-tree误以为这就是他要的TreeNode,简化的组件代码如下:

import React from 'react';
import { Tree } from 'antd';
import { treeNode, edit, deleteBtn } from './typeTree.less';
const { TreeNode: AntdTreeNode } = Tree;

export default function TreeNode(props) {
    const {
        title,
        ...rest
    } = props;

    return (
        <AntdTreeNode 
            title={
                <div className={treeNode}>
                    <span>{title}</span>
                    <span className={deleteBtn}>删除</span>
                    <span className={edit}>编辑</span>
                </div>
            }
            {...rest}
        />
    );
}

TreeNode.isTreeNode = 1;

至此,组件就正常渲染出来啦~
虽然这个封装感觉有点多余,不过献给那些与我有同样好奇心的人吧~