前言先不说别的,就说arcodesigntable的两个bug。本来就是写reacttable组件的,然后看源码学习思路,但是真的很想吐槽一下。(学习了其他组件的源码,特别是工程arco-cli的部分,受益匪浅,自己尝试写的wheel也受到了很大的启发,这个吐槽真的不是恶意的。我的tdeisgnforarco和Tencent是有期待的,因为蚂蚁家族霸道太久了,期待新鲜血液)如果arcodeisgn的团队看到这篇文章,请让写表的同学看看!!!梳理出多级表头的筛选+排序+固定逻辑。现在的写法隐患太多了。后面会写为什么现在的写法隐患很多,很容易出bug!1.这是在线bugdemocodesandbox.io/s/jovial-ka...bug展示2.继续看,我过滤了userInfo,salary大于2000的那一行完全没有效果。在线bugdemocodesandbox.io/s/competent...,说实话,如果我只是给你几个表格bug,你可以提交pr给官方。(写表的人一定要骂!!!)离谱的filtercodefilter是什么东西?让我们看看下图中的屏幕标题。我们简称为过滤器。收集受控过滤器,代码如下:const{currentFilters,currentSorter}=getDefaultFiltersAndSorter(columns);复制代码列,我们假设它看起来像这样:constcolumns=[{title:"Name",dataIndex:"name",width:140,},{title:"UserInfo",filters:[{text:">20000",value:"20000",},{text:">30000",value:"30000",},],onFilter:(value,row)=>row.salary>value,},{标题:"Information",children:[{title:"Email",dataIndex:"email",},{title:"Phone",dataIndex:"phone",},],},]复制代码getDefaultFiltersAndSorter的代码如下如下。如果不想看细节,我就直接下结论。该函数将filters受控属性、filteredValue和非受控属性defaultFilters放入currentFilters对象中,然后导出,其中key可以简单的认为是每一列上的dataIndex,即每一列的唯一标识。currentSorter我们暂时不看,但是它也为排序bug埋下了隐患。我们不会在本文中讨论对错误进行排序。函数getDefaultFiltersAndSorter(columns){constcurrentFilters={}asPartial>;constcurrentSorter={}asSorterResult;functiontravel(columns){if(columns&&columns.length>0){columns.forEach((column,index)=>{constinnerDataIndex=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[ch列名]);}});}}travel(columns);return{currentFilters,currentSorter};}复制这里的代码已经埋下了bug隐患,大家看到了吗,它会递归收集所有列上filter相关的受控和非受控属性,受控属性会覆盖非受控属性.奇怪的是,受控过滤器属性和非受控属性之间没有单独的区别。后来分析,是因为arcodeisgn有处理controlled和uncontrolled的hook,因为他现在不区分,把这个hook用错了,导致它的代码我觉得很奇怪!!一直在看!然后,他使用上面的currentFilters来const[filters,setFilters]=useState>(currentFilters);复制代码再看useColumms,它和filters密切相关,所以一定要看useColumns的实现const[groupColumns,flattenColumns]=useColumns(props);复制代码,简单描述useColumns的返回值groupColumns和flattenColumns代表什么:groupColumns,按行存储数组中的列,什么是按行,见下图第一行是姓名,用户信息,信息,薪水生日,address是第二行,Email,phone也是第二行city,road,no都是第三行flattenColumns是什么意思?它是由列叶节点组成的数组。叶子节点是指所有列中没有子属性的节点。下面是具体的代码。如果你有兴趣,你可以看看。让我们继续看吧。奇怪的代码快来了!functionuseColumns(props:TableProps):[InternalColumnProps[][],InternalColumnProps[]]{const{components,//覆盖原生表格标签rowSelection,//设置表格行是否可选,selectevent,etc.expandedRowRender,//点击展开附加行,渲染函数。当返回值为null时,展开按钮不会被渲染expandProps={},//展开参数c??olumns=[],//columnschildrenColumnName从外界传入,//默认为children}=props;//下面有个getFlattenColumns方法//getFlattenColumns展平列,因为可能有多级表头,所以需要展平//getFlattenColumns,注意这个展平只会收集叶子节点!!!!constrows:InternalColumnProps[]=useMemo(()=>getFlattenColumns(columns,childrenColumnName),[columns,childrenColumnName]);//是否是checkboxconstisCheckbox=(rowSelection&&rowSelection.type==='checkbox')||(rowSelection&&!('type'inrowSelection));//是否是radioconstisRadio=rowSelection&&rowSelection.type==='radio';//展开按钮列的宽度const{width:expandColWidth}=expandProps;//是否有expand—rowconstshouldRenderExpandCol=!!expandedRowRender;常量shouldRenderSelectionCol=isCheckbox||=useMemo(()=>getHeaderComponentOperations({selectionNode:shouldRenderSelectionCol?'holder_node':'',expandNode:shouldRenderExpandCol?'holder_node':'',}),[shouldRenderSelectionCol,shouldRenderExpandCol,getHeaderComponentOperations]);constbodyOperations=useMemo(()=>getBodyComponentOperations({selectionNode:shouldRenderSelectionCol?'holder_node':'',expandNode:shouldRenderExpandCol?'holder_node':'',}),[shouldRenderSelectionCol,shouldRenderExpandCol,getBodyComponentOperations]);//行选择。fixed表示复选框是否固定在左侧constselectionFixedLeft=rowSelection&&rowSelection.fixed;//选择列表的宽度constselectionColumnWidth=rowSelection&&rowSelection.columnWidth;constgetInternalColumns=useCallback((rows,operations,index?:number)=>{constoperationFixedProps:{fixed?:'left'|'right'}={};const_rows:InternalColumnProps[]=[];rows.forEach((r,i)=>{const_r={...r};if(!('key'inr)){_r.key=_r.dataIndex||我;}if(i===0){_r.$$isFirstColumn=true;if(_r.fixed==='left'){operationFixedProps.fixed=_r.fixed;}}else{_r.$$isFirstColumn=false;}_rows.push(_r);});constexpandColumn=shouldRenderExpandCol&&{key:INTERNAL_EXPAND_KEY,title:INTERNAL_EXPAND_KEY,width:expandColWidth,$$isOperation:true,};constselectionColumn=shouldRenderSelectionCol&&{key:INTERNAL_SELECTION_KEY,title:INTERNAL_SELECTION_KEY,width:selectionColumnWidth,$$isOperation:true,};如果(selectionFixedLeft){operationFixedProps.fixed='left';}if(typeofindex!=='number'||index===0){[...operations].reverse().forEach((operation)=>{if(operation.node){if(operation.name==='expandNode'){_rows.unshift({...expandColumn,...operationFixedProps});}elseif(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,]);constflattenColumns=useMemo(()=>getInternalColumns(rows,bodyOperations),[rows,getInternalColumns,bodyOperations]);//分割表头将列分组为n行,并加上colSpan和rowSpan,如果没有header分组则为1行//获取列的深度constrowCount=useMemo(()=>getAllHeaderRowsCount(columns,childrenColumnName),[columns,childrenColumnName]);//分行之后的行constgroupColumns=useMemo(()=>{if(rowCount===1){return[getInternalColumns(columns,headerOperations,0)];}constrows:InternalColumnProps[][]=[];consttravel=(columns,current=0)=>{行[当前]=行[当前]||[];columns.forEach((col)=>{constcolumn:InternalColumnProps={...col};if(column[childrenColumnName]){//求出叶子结点的个数就是colSpan列。colSpan=getFlattenColumns(col[childrenColumnName],childrenColumnName).length;column.rowSpan=1;rows[current].push(column);旅行(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);returnrows;},[columns,childrenColumnName,rowCount,getInternalColumns,headerOperations]);return[groupColumns,flattenColumns];}exportdefaultuseColumns;functiongetFlattenColumns(columns:InternalColumnProps[],childrenColumnName:string){const行:InternalColumnProps[]=[];functiontravel(columns){if(columns&&columns.length>0){columns.forEach((column)=>{if(!column[childrenColumnName]){rows.push({...column,key:column.键||column.dataIndex});}else{travel(column[childrenColumnName]);}});}}travel(columns);returnrows;}