【雪人日记】记一次解决issue的踩坑 ~

大家好啊,我是⛄。今天下午遇到一个很头疼的问题,业务上要给树形结构表格加一个逻辑,选择子节点的时候要求父级节点的兄弟节点及父级节点不允许选中,并且还要在不允许选中的选择框上加一个Hover的提示文字。

然后我就开始查阅Semi的文档,发现无法选中的API是支持的,但是要想有Hover操作的话需要去使用自定义的API。写到一半发现如果把筛选框一栏隐藏,他的resize的操作列还在,然后就开始一个个过用到的API,查了半天,好像没有相关的API可以操作成我这个样子,像下面这样。

Bug

简单排查API后,发现是同时使用rowSelection并且加入hidden属性,并且使用resizable属性才会触发,不可变长的表格列是正常的。所以这个是一个Bug,然后就想着自己处理一下,提了PR合并后业务的问题就解决了,2024年的第一个PR也就有啦😊。

下班回家后歇了一下就开始处理,我直接打开packages/semi-ui/table/index.tsx,直接定位到render函数去看DOM结构,发现刚好受到resizable属性影响分为了两个表格,那么我们就可以直接定位去查看ResizableTable组件。

render() {
    // eslint-disable-next-line prefer-destructuring
    const props = this.props;
    const direction = this.props.direction ?? this.context.direction;
    if (props.resizable) {
        // resizable 渲染可变长表格
        return <ResizableTable {...props} ref={this.tableRef} direction={direction} />;
    } else {
        // resizable 渲染定长表格
        return <NormalTable<RecordType> {...props} ref={this.tableRef} direction={direction} />;
    }
}

然后因为我们的BUG是直接多了一个无法删除的列,所以我猜测是除了我们自己传入的columns,Table内部还会对我们传入的columns进行拓展,才能在最后渲染的时候统一使用一个columns数组,所以我们需要从columns入手,同样先去看看DOM结构函数:

return <Table {...restProps} columns={finalColumns} components={components} ref={ref} />;

发现colums确实不是用户传入的,内部进行了处理,用到了finalColumns,然后就顺着往上找发现使用了assignResizableRender函数处理colums,但是这个函数并没有给我们colums新增列啊,从名称推断来看就只是加了一些让colums可改变长度的配置,然后我就想可能是在Table组件里还会处理,那就继续看Table组件,继续定位DOM结构,继续尝试去找colums

//...
<React.Fragment key={'pagination-top'}>
    {['top', 'both'].includes(paginationPosition) ? tablePagination : null}
</React.Fragment>
{this.renderTitle({
    title: (props as any).title,
    dataSource: props.dataSource,
    prefixCls: props.prefixCls,
})}
// renderMainTable 肯定就是 Table 的主体部分
<div className={`${prefixCls}-container`}>{this.renderMainTable({ ...props })}</div>
<React.Fragment key={'pagination-bottom'}>
    {['bottom', 'both'].includes(paginationPosition) ? tablePagination : null}
</React.Fragment>
// ...

顺着renderMainTable继续往下找最后可以找到是在handleColumns第一层处理了我们的columns(真的好多层处理😂),然后我们可以发现里面竟然有我们用到的API,刚好就是rowSelection和其中的hidden,并且也确实通过了normalizeSelectionColumn增加了一列选择列!

// selection column
if (rowSelection && !get(rowSelection, 'hidden')) {
    // 找找 columns 里面有没有选择框列
    const destIndex = findIndex(columns, item => item.key === strings.DEFAULT_KEY_COLUMN_SELECTION);
    // 创建一个选择框列的配置
    const column = this.normalizeSelectionColumn({ rowSelection, prefixCls });
	
    if (destIndex > -1) { // 如果已经有了合并配置
        columns[destIndex] = { ...column, ...columns[destIndex] };
    } else if (column.fixed === 'right') { // 没有并新增到最后一列
        columns = [...columns, column];
    } else { // 没有并新增到最左侧一列
        columns = [column, ...columns];
    }
}

但是,!get(rowSelection, 'hidden')这个条件确实没问题啊,隐藏就不加选择框列了,然后我就蒙了,这能从哪里来呢。我们可以看到选择框列的key是这个DEFAULT_KEY_COLUMN_SELECTION,我就尝试性的搜了一下,在ResizableTable里面找到这一段代码:

// 如果有 rowSelection 且没有添加选择框列那就添加一个
if (props.rowSelection && !find(rawColumns, item => item.key === strings.DEFAULT_KEY_COLUMN_SELECTION)) {
    newColumns.unshift({
        width: get(props, 'rowSelection.width', numbers.DEFAULT_WIDTH_COLUMN_SELECTION),
        key: strings.DEFAULT_KEY_COLUMN_SELECTION,
    });
}

😂,原来在这里也处理过一次,刚刚看的收光顾着看finalColumns了,确实没发现这个newColumns,还有这个const [columns, setColumns] = useState(newColumns);,并且这里还有注释的,说了会新增一个【选择列】,真的是心态爆炸,没细看的后果,导致绕了一下。

    /**
     * 此处关于 columns 有三个存储
	...
     * 2. newColumns 是 rawColumns 的深拷贝,同时根据 props.expandedRowRender、props.hideExpandedColumn 和 props.rowSelection
     * 这三个参数加入了【选择列】以及【展开列】
	...
     */

我们可以发现在这里的逻辑并没有添加hidden参数相关的处理,那么这要在这里加上判断,hidden=true时不进行添加选择框列即可。

// 用到 lodash 的 get 函数
if (props.rowSelection && !get(props.rowSelection, 'hidden') && !find(rawColumns, item => item.key === strings.DEFAULT_KEY_COLUMN_SELECTION)) {
    newColumns.unshift({
        width: get(props, 'rowSelection.width', numbers.DEFAULT_WIDTH_COLUMN_SELECTION),
        key: strings.DEFAULT_KEY_COLUMN_SELECTION,
    });
}

最后在StoryBook里面预览一下发现没问题,跑一下单测也没问题,好耶😀,直接拿下。打算提个issue然后第二天再加个示实例再提交啥的,打开issues的瞬间就看到了这一条,点进去一看,已经解决了。然后看了一下改动,确实是和刚刚想的解决思路一样😂,这就很尴尬了,大晚上回来看半天,一下睡不着了,浅浅记录一下。

issue

所以说记一次解决issue的踩坑,踩的是什么坑呢:遇到问题先去查一查issues😂。

哈哈哈,浅浅分享一下解决问题的思路,对一个完全不了解的项目,要从遇到问题的API和DOM结构入手💪。

还想着不能熬夜了😂,搞上头了,睡觉睡觉💤