前言
先不说别的,上两个arco design table的bug。原本是写react table组件,而后看源码学习思路,后果看的我真的很想吐槽。(其余组件我在学习源码上受益匪浅,尤其是工程化arco-cli那局部,我本人尝试写的轮子也是受到很多启发,这个吐槽并不是真的有歹意,我对arco和腾讯的tdeisgn是有期待的,因为ant一家独大太久了,很期待陈腐的血液)
如果arco deisgn的团队看到这篇文章,请肯定让写table的同学看一下!!!把多级表头的筛选 + 排序 + 固定逻辑好好梳理一下,目前的写法隐患太多了,我前面会写为什么目前的写法隐患很多,非常容易出bug!
1、这是在线bug demo codesandbox.io/s/jovial-ka…

bug显示

2、持续看,我筛选userInfo上,工资大于2000的行,基本没成果
在线bug 的demo codesandbox.io/s/competent…

说实话,我轻易送给大家几个table的bug,都能够去给官网提pr了。(这个写table的人肯定要好好的批评一下!!!!)
离谱的filter代码
filter是啥呢,咱们看下图
这个表头的筛选咱们简称为filter

首先官网把columns上所有的受控和非受控的filter收集起来,代码如下:
const { currentFilters, currentSorter } = getDefaultFiltersAndSorter(columns);
复制代码
columns咱们假如长成这样:
const columns = [
{

title: "Name",dataIndex: "name",width: 140,

},
{

title: "User Info",filters: [  {    text: "> 20000",    value: "20000",  },  {    text: "> 30000",    value: "30000",  },],onFilter: (value, row) => row.salary > value,

},
{

title: "Information",children: [  {    title: "Email",    dataIndex: "email",  },  {    title: "Phone",    dataIndex: "phone",  },],

},
]
复制代码
getDefaultFiltersAndSorter的代码如下,不想看细节的,我就说下结论,这个函数是把filters受控属性,filteredValue和非受控属性defaultFilters放到currentFilters对象里,而后导出,其中key能够简略认为是每个columns上的dataIndex,也就是每一列的惟一标识符。
currentSorter咱们临时不看,也是为排序的bug埋下隐患,咱们这篇文章先不谈排序的bug。
function getDefaultFiltersAndSorter(columns) {

const currentFilters = {} as Partial<Record<keyof T, string[]>>;const currentSorter = {} as SorterResult;function travel(columns) {  if (columns && columns.length > 0) {    columns.forEach((column, index) => {      const innerDataIndex = column.dataIndex === undefined ? index : column.dataIndex;      if (!column[childrenColumnName]) {        if (column.defaultFilters) {          currentFilters[innerDataIndex] = column.defaultFilters;        }        if (column.filteredValue) {          currentFilters[innerDataIndex] = column.filteredValue;        }        if (column.defaultSortOrder) {          currentSorter.field = innerDataIndex;          currentSorter.direction = column.defaultSortOrder;        }        if (column.sortOrder) {          currentSorter.field = innerDataIndex;          currentSorter.direction = column.sortOrder;        }      } else {        travel(column[childrenColumnName]);      }    });  }}travel(columns);return { currentFilters, currentSorter };

}

复制代码
这里的曾经为出bug埋下隐患了,大家看啊,它是递归收集所有columns上的filter相干的受控和非受控的属性,而且受控的属性会笼罩非受控。
这里没有独自辨别受控的filter属性和非受控的属性就很奇怪。前面剖析,因为arco deisgn有个专门解决受控和非受控的hooks,因为他当初不辨别,还用错这个hooks,造成我看起来它的代码奇怪的要命!!
接着看!
而后,他用下面的currentFilters去
const [filters, setFilters] = useState<FilterType<T>>(currentFilters);
复制代码
接着看一下useColunms,这个跟filters前面非亲非故,所以咱们必须要看下useColumns的实现
const [groupColumns, flattenColumns] = useColumns<T>(props);
复制代码
简略形容一下useColumns的返回值 groupColumns, flattenColumns别离代表什么:

groupColumns,它将columns按行存储到数组外面,啥是按行呢,看下图

name、user info、Information、salary是第一行
Birthday、address是第二行,Email,phone也是第二行
city、road、no是第三行

flattenColumns是啥意思呢?就是columns叶子节点组成的数组,叶子节点是指所有columns中没有children属性的节点。以下是具体代码,有趣味的能够看看,咱们接着看,马上很奇怪的代码就要来了!
function useColumns<T>(props: TableProps<T>): [InternalColumnProps[][], InternalColumnProps[]] {
const {

components, // 笼罩原生表格标签rowSelection, // 设置表格行是否可选,选中事件等expandedRowRender, // 点击开展额定的行,渲染函数。返回值为 null 时,不会渲染开展按钮expandProps = {}, // 开展参数columns = [], // 外界传入的columnschildrenColumnName, // 默认是children

} = props;


// 上面有getFlattenColumns办法
// getFlattenColumns平铺columns,因为可能有多级表头,所以须要平铺
// getFlattenColumns,留神这个平铺只会收集叶子节点!!!!
const rows: InternalColumnProps[] = useMemo(

() => getFlattenColumns(columns, childrenColumnName),[columns, childrenColumnName]

);

// 是否是checkbox
const isCheckbox =

(rowSelection && rowSelection.type === 'checkbox') ||(rowSelection && !('type' in rowSelection));

// 是否是radio
const isRadio = rowSelection && rowSelection.type === 'radio';

// 开展按钮列的宽度
const { width: expandColWidth } = expandProps;

// 是否有expand—row
const shouldRenderExpandCol = !!expandedRowRender;
const shouldRenderSelectionCol = isCheckbox || isRadio;

// 获取到自定义的操作栏,默认是selectNode和expandNode
const { getHeaderComponentOperations, getBodyComponentOperations } = useComponent(components);

const headerOperations = useMemo(

() =>  getHeaderComponentOperations({    selectionNode: shouldRenderSelectionCol ? 'holder_node' : '',    expandNode: shouldRenderExpandCol ? 'holder_node' : '',  }),[shouldRenderSelectionCol, shouldRenderExpandCol, getHeaderComponentOperations]

);
const bodyOperations = useMemo(

() =>  getBodyComponentOperations({    selectionNode: shouldRenderSelectionCol ? 'holder_node' : '',    expandNode: shouldRenderExpandCol ? 'holder_node' : '',  }),[shouldRenderSelectionCol, shouldRenderExpandCol, getBodyComponentOperations]

);

// rowSelection.fixed 示意checkbox是否固定在右边
const selectionFixedLeft = rowSelection && rowSelection.fixed;
// 抉择列的宽度
const selectionColumnWidth = rowSelection && rowSelection.columnWidth;

const getInternalColumns = useCallback(

(rows, operations, index?: number) => {  const operationFixedProps: { fixed?: 'left' | 'right' } = {};  const _rows: InternalColumnProps[] = [];  rows.forEach((r, i) => {    const _r = { ...r };    if (!('key' in r)) {      _r.key = _r.dataIndex || i;    }    if (i === 0) {      _r.$$isFirstColumn = true;      if (_r.fixed === 'left') {        operationFixedProps.fixed = _r.fixed;      }    } else {      _r.$$isFirstColumn = false;    }    _rows.push(_r);  });  const expandColumn = shouldRenderExpandCol && {    key: INTERNAL_EXPAND_KEY,    title: INTERNAL_EXPAND_KEY,    width: expandColWidth,    $$isOperation: true,  };  const selectionColumn = shouldRenderSelectionCol && {    key: INTERNAL_SELECTION_KEY,    title: INTERNAL_SELECTION_KEY,    width: selectionColumnWidth,    $$isOperation: true,  };  if (selectionFixedLeft) {    operationFixedProps.fixed = 'left';  }  if (typeof index !== 'number' || index === 0) {    [...operations].reverse().forEach((operation) => {      if (operation.node) {        if (operation.name === 'expandNode') {          _rows.unshift({ ...expandColumn, ...operationFixedProps });        } else if (operation.name === 'selectionNode') {          _rows.unshift({ ...selectionColumn, ...operationFixedProps });        } else {          _rows.unshift({            ...operation,            ...operationFixedProps,            title: operation.name,            key: operation.name,            $$isOperation: true,            width: operation.width || 40,          });        }      }    });  }  return _rows;},[  expandColWidth,  shouldRenderExpandCol,  shouldRenderSelectionCol,  selectionColumnWidth,  selectionFixedLeft,]

);

const flattenColumns = useMemo(

() => getInternalColumns(rows, bodyOperations),[rows, getInternalColumns, bodyOperations]

);

// 把表头分组的 columns 分成 n 行,并且加上 colSpan 和 rowSpan,没有表头分组的话是 1 行。
// 获取column的深度
const rowCount = useMemo(

() => getAllHeaderRowsCount(columns, childrenColumnName),[columns, childrenColumnName]

);

// 分行之后的rows
const groupColumns = useMemo(() => {

if (rowCount === 1) {  return [getInternalColumns(columns, headerOperations, 0)];}const rows: InternalColumnProps[][] = [];const travel = (columns, current = 0) => {  rows[current] = rows[current] || [];  columns.forEach((col) => {    const column: InternalColumnProps = { ...col };    if (column[childrenColumnName]) {      // 求出叶子结点的个数就是colSpan      column.colSpan = getFlattenColumns(col[childrenColumnName], childrenColumnName).length;      column.rowSpan = 1;      rows[current].push(column);      travel(column[childrenColumnName], current + 1);    } else {      column.colSpan = 1;      // 这是      column.rowSpan = rowCount - current;      rows[current].push(column);    }  });  rows[current] = getInternalColumns(rows[current], headerOperations, current);};travel(columns);return rows;

}, [columns, childrenColumnName, rowCount, getInternalColumns, headerOperations]);

return [groupColumns, flattenColumns];
}

export default useColumns;

function getFlattenColumns(columns: InternalColumnProps[], childrenColumnName: string) {
const rows: InternalColumnProps[] = [];
function travel(columns) {

if (columns && columns.length > 0) {  columns.forEach((column) => {    if (!column[childrenColumnName]) {      rows.push({ ...column, key: column.key || column.dataIndex });    } else {      travel(column[childrenColumnName]);    }  });}

}
travel(columns);

return rows;
}