前端框架arco?table源碼遇到的問(wèn)題解析
前言
先不說(shuō)別的,上兩個(gè)arco design table的bug。本來(lái)是寫react table組件,然后看源碼學(xué)習(xí)思路,結(jié)果看的我真的很想吐槽。(其他組件我在學(xué)習(xí)源碼上受益匪淺,尤其是工程化arco-cli那部分,我自己嘗試寫的輪子也是受到很多啟發(fā),這個(gè)吐槽并不是真的有惡意,我對(duì)arco和騰訊的tdeisgn是有期待的,因?yàn)閍nt一家獨(dú)大太久了,很期待新鮮的血液)
如果arco deisgn的團(tuán)隊(duì)看到這篇文章,請(qǐng)一定讓寫table的同學(xué)看一下?。。“讯嗉?jí)表頭的篩選 + 排序 + 固定邏輯好好梳理一下,目前的寫法隱患太多了,我后面會(huì)寫為什么目前的寫法隱患很多,非常容易出bug!
1、這是在線bug demo codesandbox.io/s/jovial-ka…

bug顯示

2、繼續(xù)看,我篩選userInfo上,工資大于2000的行,根本沒(méi)效果
在線bug 的demo codesandbox.io/s/competent…

說(shuō)實(shí)話,我隨便送給大家?guī)讉€(gè)table的bug,都可以去給官方提pr了。(這個(gè)寫table的人一定要好好的批評(píng)一下!?。。。?/p>
離譜的filter代碼
filter是啥呢,我們看下圖
這個(gè)表頭的篩選我們簡(jiǎn)稱為filter

首先官方把columns上所有的受控和非受控的filter收集起來(lái),代碼如下:
const { currentFilters, currentSorter } = getDefaultFiltersAndSorter(columns);
columns我們假設(shè)長(zhǎng)成這樣:
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的代碼如下,不想看細(xì)節(jié)的,我就說(shuō)下結(jié)論,這個(gè)函數(shù)是把filters受控屬性,filteredValue和非受控屬性defaultFilters放到currentFilters對(duì)象里,然后導(dǎo)出,其中key可以簡(jiǎn)單認(rèn)為是每個(gè)columns上的dataIndex,也就是每一列的唯一標(biāo)識(shí)符。
currentSorter我們暫時(shí)不看,也是為排序的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 };
}
這里的已經(jīng)為出bug埋下隱患了,大家看啊,它是遞歸收集所有columns上的filter相關(guān)的受控和非受控的屬性,而且受控的屬性會(huì)覆蓋非受控。
這里沒(méi)有單獨(dú)區(qū)分受控的filter屬性和非受控的屬性就很奇怪。后面分析,因?yàn)閍rco deisgn有個(gè)專門處理受控和非受控的hooks,因?yàn)樗F(xiàn)在不區(qū)分,還用錯(cuò)這個(gè)hooks,造成我看起來(lái)它的代碼奇怪的要命??!
接著看!
然后,他用上面的currentFilters去
const [filters, setFilters] = useState<FilterType<T>>(currentFilters);
接著看一下useColunms,這個(gè)跟filters后面息息相關(guān),所以我們必須要看下useColumns的實(shí)現(xiàn)
const [groupColumns, flattenColumns] = useColumns<T>(props);
簡(jiǎn)單描述一下useColumns的返回值 groupColumns, flattenColumns分別代表什么:
- groupColumns,它將columns按行存儲(chǔ)到數(shù)組里面,啥是按行呢,看下圖

- name、user info、Information、salary是第一行
- Birthday、address是第二行,Email,phone也是第二行
- city、road、no是第三行
flattenColumns是啥意思呢?就是columns葉子節(jié)點(diǎn)組成的數(shù)組,葉子節(jié)點(diǎn)是指所有columns中沒(méi)有children屬性的節(jié)點(diǎn)。以下是具體代碼,有興趣的可以看看,我們接著看,馬上很奇怪的代碼就要來(lái)了!
function useColumns<T>(props: TableProps<T>): [InternalColumnProps[][], InternalColumnProps[]] {
const {
components, // 覆蓋原生表格標(biāo)簽
rowSelection, // 設(shè)置表格行是否可選,選中事件等
expandedRowRender, // 點(diǎn)擊展開額外的行,渲染函數(shù)。返回值為 null 時(shí),不會(huì)渲染展開按鈕
expandProps = {}, // 展開參數(shù)
columns = [], // 外界傳入的columns
childrenColumnName, // 默認(rèn)是children
} = props;

// 下面有g(shù)etFlattenColumns方法
// getFlattenColumns平鋪columns,因?yàn)榭赡苡卸嗉?jí)表頭,所以需要平鋪
// getFlattenColumns,注意這個(gè)平鋪只會(huì)搜集葉子節(jié)點(diǎn)?。。?!
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;
// 獲取到自定義的操作欄,默認(rèn)是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,沒(méi)有表頭分組的話是 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]) {
// 求出葉子結(jié)點(diǎn)的個(gè)數(shù)就是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;
}
接下來(lái)這個(gè)函數(shù)求的是受控的filters的集合!
疑問(wèn)1:
為啥你受控的集合不在上面我們提到的getDefaultFiltersAndSorter里面就求出來(lái),非要自己?jiǎn)为?dú)再求一遍?
const controlledFilter = useMemo(() => {
// 允許 filteredValue 設(shè)置為 undefined 表示不篩選
const flattenFilteredValueColumns = flattenColumns.filter(
(column) => 'filteredValue' in column
);
const newFilters = {};
// 受控的篩選,當(dāng)columns中的篩選發(fā)生改變時(shí),更新state
if (flattenFilteredValueColumns.length) {
flattenFilteredValueColumns.forEach((column, index) => {
const innerDataIndex = column.dataIndex === undefined ? index : column.dataIndex;
if (innerDataIndex !== undefined) {
newFilters[innerDataIndex] = column.filteredValue;
}
});
}
return newFilters;
}, [flattenColumns]);
結(jié)果我們一看,flattenColumns里去拿受控的columns屬性的值,而flattenColumns是拿的葉子節(jié)點(diǎn),這么說(shuō)來(lái),這個(gè)controlledFilter還是跟之前的getDefaultFiltersAndSorter里的currentFilters是有區(qū)別的,一個(gè)是葉子節(jié)點(diǎn),一個(gè)是全部的columns。
但是!問(wèn)題來(lái)了,你只求葉子節(jié)點(diǎn)的受控屬性,那非葉子節(jié)點(diǎn)的受控屬性萬(wàn)一用戶給你賦值了,豈不是沒(méi)有作用了?。?!
這就是我們最開始提到的第二個(gè)bug的根本原因,你自己最開始求得是所有columns中的filters的集合,現(xiàn)在用的是葉子節(jié)點(diǎn)的filters的屬性,這不是牛頭不對(duì)馬嘴嗎???
打不全補(bǔ)丁
接著看,上面的離譜邏輯導(dǎo)致后面的代碼想去打補(bǔ)丁,結(jié)果就是打不全補(bǔ)??!
const innerFilters = useMemo<FilterType<T>>(() => {
return Object.keys(controlledFilter).length ? controlledFilter : filters;
}, [filters, controlledFilter]);
你看,他去得到一個(gè)innerFilters,咋求的呢?如果controlledFilter有值,也就是葉子節(jié)點(diǎn)有filter的受控屬性,那么就用葉子節(jié)點(diǎn)的受控屬性作為我們要使用的filters,但是?。。。?/p>
如果沒(méi)有葉子節(jié)點(diǎn)的受控屬性的filters,他居然用的是filters,filters是咋求出來(lái)的,不就是最上面的getDefaultFiltersAndSorter嗎,這個(gè)函數(shù)求的是所有columns里filters的集合。
這個(gè)函數(shù)就非常非常離譜,為啥邏輯不對(duì)啊,一個(gè)針對(duì)葉子節(jié)點(diǎn),一個(gè)針對(duì)全部節(jié)點(diǎn)!!!
更大的問(wèn)題
// stateCurrentFilter 標(biāo)記了下拉框中選中的 filter 項(xiàng)目,在受控模式下它與 currentFilter 可以不同
const [currentFilter, setCurrentFilter, stateCurrentFilter] = useMergeValue<string[]>([], {
value: currentFilters[innerDataIndex] || [],
});
注意,這里有個(gè)useMergeValue的hooks,這個(gè)hooks 在arco deisgn中起著舉足輕重的作用,我們必須好好說(shuō)一下這個(gè)hooks,再看看寫這個(gè)組件的同學(xué)為什么用錯(cuò)了!
我們簡(jiǎn)單解釋一下這個(gè)hooks的目的,我們?cè)谟媒M件的時(shí)候一般會(huì)有兩種模式,受控組件和非受控,這個(gè)hooks就是完美解決這個(gè)問(wèn)題,你只要把value傳入受控組件的屬性,defaultValue傳入非受控屬性,這個(gè)hooks就自動(dòng)接管了這兩種狀態(tài)的變化,非常棒的hooks,寫的人真的很不錯(cuò)!
import React, { useState, useEffect, useRef } from 'react';
import { isUndefined } from '../is';
export default function useMergeValue<T>(
defaultStateValue: T,
props?: {
defaultValue?: T;
value?: T;
}
): [T, React.Dispatch<React.SetStateAction<T>>, T] {
const { defaultValue, value } = props || {};
const firstRenderRef = useRef(true);
const [stateValue, setStateValue] = useState<T>(
!isUndefined(value) ? value : !isUndefined(defaultValue) ? defaultValue : defaultStateValue
);
useEffect(() => {
// 第一次渲染時(shí)候,props.value 已經(jīng)在useState里賦值給stateValue了,不需要再次賦值。
if (firstRenderRef.current) {
firstRenderRef.current = false;
return;
}
// 外部value等于undefined,也就是一開始有值,后來(lái)變成了undefined(
// 可能是移除了value屬性,或者直接傳入的undefined),那么就更新下內(nèi)部的值。
// 如果value有值,在下一步邏輯中直接返回了value,不需要同步到stateValue
if (value === undefined) {
setStateValue(value);
}
}, [value]);
const mergedValue = isUndefined(value) ? stateValue : value;
return [mergedValue, setStateValue, stateValue];
}
從這個(gè)hooks導(dǎo)出的[mergedValue, setStateValue, stateValue],我們簡(jiǎn)單分析下怎么用,mergedValue是以受控為準(zhǔn)的,也就是外部發(fā)現(xiàn)如果用了受控屬性,取這個(gè)值就行了,而且因?yàn)閡seEffect監(jiān)聽了value的變化,你就不用管受控屬性的變化了,自動(dòng)處理,多好??!
然后setStateValue主要是手動(dòng)去更新stateValue的,主要是在非受控的條件下去更新值,所以stateValue一般也是外部判斷,如果是非受控條件,就取這個(gè)值!
我們接著看arco deisgn中這個(gè)人咋用的,完全浪費(fèi)了這么一個(gè)好hook。
// stateCurrentFilter 標(biāo)記了下拉框中選中的 filter 項(xiàng)目,在受控模式下它與 currentFilter 可以不同
const [currentFilter, setCurrentFilter, stateCurrentFilter] = useMergeValue<string[]>([], {
value: currentFilters[innerDataIndex] || [],
});
value傳了一個(gè)currentFilters[innerDataIndex] ,currentFilters是指所有columns里有可能是filters受控的屬性集合,有可能是非受控filters屬性的集合,innerDataIndex值的當(dāng)前列的dataindex,也就是唯一標(biāo)識(shí)符key。
那么問(wèn)題來(lái)了value明明人家建議你傳的是受控!受控!受控屬性的值啊,因?yàn)槟鉩urrentFilters目前既可能是受控,也可能是非受控,所以你傳給value是沒(méi)有辦法的辦法,因?yàn)槟銈鹘odefaultValue也不對(duì)!
錯(cuò)誤的繼續(xù)
useEffect(() => {
setCurrentFilter(currentFilters[innerDataIndex] || []);
}, [currentFilters, innerDataIndex]);
useEffect(() => {
if (currentFilter && currentFilter !== stateCurrentFilter) {
setCurrentFilter(currentFilter);
}
}, [filterVisible]);
第一個(gè)useEffect是賦值了一個(gè)不知道是受控還是非受控的filters,然后第二個(gè)假設(shè)currentFilter存在,就是說(shuō)如果受控的filters存在就賦值給優(yōu)先級(jí)更高的受控屬性!
顯而易見的問(wèn)題
上面造成兩個(gè)useEffect的原因,不就是最開始在收集filters的時(shí)候,沒(méi)有區(qū)分受控和非受控filters,然后后面代碼再求一遍嗎,而且求的邏輯讓人不好看懂,對(duì)不起,我想說(shuō)這代碼寫的,太容易出bug了,寫的這個(gè)人真的是一己之力把table組件毀了?。?!
然后我們看filters在確定篩選時(shí)的函數(shù)!
/** ----------- Filters ----------- */
function onHandleFilter(column, filter: string[]) {
const newFilters = {
...innerFilters,
[column.dataIndex]: filter,
};
const mergedFilters = {
...newFilters,
...controlledFilter,
};
if (isArray(filter) && filter.length) {
setFilters(mergedFilters);
const newProcessedData = getProcessedData(innerSorter, newFilters);
const currentData = getPageData(newProcessedData);
onChange &&
onChange(getPaginationProps(newProcessedData), innerSorter, newFilters, {
currentData,
action: 'filter',
});
} else if (isArray(filter) && !filter.length) {
onHandleFilterReset(column);
}
}
搞笑操作再次上演,innerFilters本來(lái)就是個(gè)奇葩,然后用
[column.dataIndex]: filter
去覆蓋innerFilters里的dataIndex里的filter,這里的filter本來(lái)就是非受控的屬性,你完全不區(qū)分受控非受控就上去一頓合并,萬(wàn)一是受控的屬性呢??????
然后在mergedFilters里居然用controlledFilter再次去亡羊補(bǔ)牢,想用假如說(shuō)有受控的filters,那么就優(yōu)先用受控的值去覆蓋innerFilters。
結(jié)尾
最開始不區(qū)分受控和非受控filters,后面全是一頓補(bǔ)?。∧汩_始區(qū)分不就代碼邏輯就很清晰了嗎,造成這么多次的遍歷columns還有很多多余的更新react組件,讓我忍不住想吐槽一下?。?!
如何改進(jìn),有興趣的同學(xué)可以去提pr
我簡(jiǎn)單寫一下如何解決最開始寫的第二個(gè)bug。
首先,getDefaultFiltersAndSorter要區(qū)分受控和非受控的情況,這是給后面的useMergeProps傳遞給受控和非受控屬性做鋪墊,題外話!大家寫組件庫(kù)的話可以copy一份useMergeProps這個(gè)hook,真的好東西!改進(jìn)如下:
// currentFilteredValues代表非受控的filters的全部收集器
// currentDefaultFilters代表受控的filters的全部收集器
const { currentFilteredValues, currentDefaultFilters, currentSorter } =
getDefaultFiltersAndSorter(columns);
function getDefaultFiltersAndSorter(columns) {
const currentFilteredValues = {} as Partial<Record<keyof T, string[]>>;
const currentDefaultFilters = {} 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) {
currentDefaultFilters[innerDataIndex] = column.defaultFilters;
}
// 篩選的受控屬性,值為篩選的value數(shù)組 string[]
if (column.filteredValue) {
currentFilteredValues[innerDataIndex] = column.filteredValue;
}
// 默認(rèn)排序方式 'ascend' | 'descend'
if (column.defaultSortOrder) {
currentSorter.field = innerDataIndex;
currentSorter.direction = column.defaultSortOrder;
}
// 排序的受控屬性,可以控制列的排序,可設(shè)置為 ascend descend
if (column.sortOrder) {
currentSorter.field = innerDataIndex;
currentSorter.direction = column.sortOrder;
}
} else {
// 篩選的非受控寫法
if (column.defaultFilters) {
currentDefaultFilters[innerDataIndex] = column.defaultFilters;
}
// 篩選的受控屬性,值為篩選的value數(shù)組 string[]
if (column.filteredValue) {
currentFilteredValues[innerDataIndex] = column.filteredValue;
}
// 默認(rèn)排序方式 'ascend' | 'descend'
if (column.defaultSortOrder) {
currentSorter.field = innerDataIndex;
currentSorter.direction = column.defaultSortOrder;
}
// 排序的受控屬性,可以控制列的排序,可設(shè)置為 ascend descend
if (column.sortOrder) {
currentSorter.field = innerDataIndex;
currentSorter.direction = column.sortOrder;
}
travel(column[childrenColumnName]);
}
});
}
}
travel(columns);
return { currentFilteredValues, currentDefaultFilters, currentSorter };
}
然后初始化filters的時(shí)候,就要簡(jiǎn)單判斷一下,我這里寫的很爛,因?yàn)槌槌鲆粋€(gè)函數(shù)的,主要是自己當(dāng)初為了跑通代碼,隨便寫了下,意思大家懂就行
const [filters, setFilters] = useState<FilterType<T>>(
Object.keys(currentDefaultFilters).length
? currentDefaultFilters
: Object.keys(currentFilteredValues).length
? currentFilteredValues
: undefined
);
然后在 columns文件里,useMergeValue做受控屬性和非受控屬性的收口,因?yàn)橹拔覀儏^(qū)分了受控和非受控讓后面的代碼邏輯清晰很多。
const innerDataIndex = dataIndex === undefined ? index : dataIndex;
// stateCurrentFilter 標(biāo)記了下拉框中選中的 filter 項(xiàng)目,在受控模式下它與 currentFilter 可以不同
// currentFilter是受控value, setCurrentFilter主要是給非受控value的, stateCurrentFilter 內(nèi)部value
const [currentFilter, setCurrentFilter] = useMergeValue<string[]>([], {
value: currentFilteredValues[innerDataIndex],
defaultValue: currentDefaultFilters[innerDataIndex],
});
然后點(diǎn)擊filters的時(shí)候如何排序呢,這里filters就是受控和非受控的合并體,再用 [column.dataIndex]: filter更新當(dāng)前最新的filter,后面更新數(shù)據(jù)就很自然了,getProcessedData是計(jì)算filters后的列,這個(gè)函數(shù)也需要改一下,把只計(jì)算葉子節(jié)點(diǎn)的改為計(jì)算所有的columns
function onHandleFilter(column, filter: string[]) {
const mergedFilters = {
...filters,
[column.dataIndex]: filter, // 篩選項(xiàng)
};
if (isArray(filter) && filter.length) {
setFilters(mergedFilters);
const newProcessedData = getProcessedData(innerSorter, mergedFilters);
const currentData = getPageData(newProcessedData);
onChange &&
onChange(getPaginationProps(newProcessedData), innerSorter, mergedFilters, {
currentData: getOriginData(currentData),
action: 'filter',
});
} else if (isArray(filter) && !filter.length) {
onHandleFilterReset(column);
}
}以上就是前端框架arco table源碼遇到的問(wèn)題解析的詳細(xì)內(nèi)容,更多關(guān)于前端框架arco table解析的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序 視圖層(xx.xml)和邏輯層(xx.js)詳細(xì)介紹
這篇文章主要介紹了微信小程序 視圖層(xx.xml)和邏輯層(xx.js)詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-10-10
微信小程序 頁(yè)面跳轉(zhuǎn)如何實(shí)現(xiàn)傳值
這篇文章主要介紹了微信小程序 頁(yè)面跳轉(zhuǎn)如何實(shí)現(xiàn)傳值的相關(guān)資料,需要的朋友可以參考下2017-04-04
深入內(nèi)存原理談JS中變量存儲(chǔ)在堆中還是棧中
JavaScript中基本類型存儲(chǔ)在堆中還是棧中,百度一下有很多不同的答案,本篇文章就來(lái)給大家為此做個(gè)詳細(xì)的介紹,需要的朋友可以參考一下2021-09-09
ECharts框架Sunburst拖拽功能實(shí)現(xiàn)方案詳解
這篇文章主要為大家介紹了ECharts框架Sunburst拖拽功能實(shí)現(xiàn)方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
axios進(jìn)度條onDownloadProgress函數(shù)total參數(shù)undefined解決分析
這篇文章主要介紹了axios進(jìn)度條onDownloadProgress函數(shù)total參數(shù)undefined解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
JavaScript立即執(zhí)行函數(shù)用法解析
這篇文章主要介紹了JavaScript立即執(zhí)行函數(shù),我們知道,在一般情況下,函數(shù)必須先調(diào)用才能執(zhí)行,如下所示,我們定義了一個(gè)函數(shù),并且調(diào)用,下面一起進(jìn)入文章來(lái)接具體的使用方法吧2021-12-12

