diff --git a/.changeset/curly-beers-know.md b/.changeset/curly-beers-know.md new file mode 100644 index 000000000..51751e78e --- /dev/null +++ b/.changeset/curly-beers-know.md @@ -0,0 +1,5 @@ +--- +"@hi-ui/hiui": patch +--- + +perf(check-tree-select): 优化大数据下勾选卡顿 diff --git a/.changeset/little-bananas-cry.md b/.changeset/little-bananas-cry.md new file mode 100644 index 000000000..7dcd60a44 --- /dev/null +++ b/.changeset/little-bananas-cry.md @@ -0,0 +1,7 @@ +--- +"@hi-ui/check-tree-select": patch +"@hi-ui/tag-input": patch +"@hi-ui/tree": patch +--- + +perf: 优化大数据下勾选卡顿 diff --git a/packages/ui/check-tree-select/src/hooks/use-check.ts b/packages/ui/check-tree-select/src/hooks/use-check.ts index 1c2f1e7f5..ba2f817fe 100644 --- a/packages/ui/check-tree-select/src/hooks/use-check.ts +++ b/packages/ui/check-tree-select/src/hooks/use-check.ts @@ -30,11 +30,16 @@ export const useCheck = ( // 搜索时临时选中缓存数据 const [checkedNodes, setCheckedNodes] = useState([]) + const flattedDataMap = useMemo(() => new Map(flattedData.map((item) => [item.id, item])), [ + flattedData, + ]) + const [checkedIds, trySetCheckedIds] = useUncontrolledState( defaultCheckedIds, checkedIdsProp, (checkedIds, checkedNode, checked, semiCheckedIds) => { - const nextCheckedNodes = flattedData.filter((item) => checkedIds.includes(item.id)) + const checkedIdsSet = new Set(checkedIds) + const nextCheckedNodes = flattedData.filter((item) => checkedIdsSet.has(item.id)) setCheckedNodes(nextCheckedNodes) onCheck?.(checkedIds, { @@ -69,12 +74,12 @@ export const useCheck = ( const proxyOnNodeCheck = useCallback( (target: FlattedCheckTreeSelectDataItem, shouldChecked: boolean) => { // 保证 target 来源于原数据自身,而不是tree内部 - const targetNode = flattedData.find((item) => item.id === target.id) + const targetNode = flattedDataMap.get(target.id) if (targetNode) { onNodeCheck(targetNode, shouldChecked) } }, - [onNodeCheck, flattedData] + [flattedDataMap, onNodeCheck] ) return [checkedIds, trySetCheckedIds, proxyOnNodeCheck, checkedNodes, parsedCheckedIds] as const diff --git a/packages/ui/check-tree-select/src/utils/index.ts b/packages/ui/check-tree-select/src/utils/index.ts index 89c9463b1..bb4202ba0 100644 --- a/packages/ui/check-tree-select/src/utils/index.ts +++ b/packages/ui/check-tree-select/src/utils/index.ts @@ -1,5 +1,5 @@ import React from 'react' -import { fFindNodeById, findNestedChildren, getNodeAncestors } from '@hi-ui/tree-utils' +import { findNestedChildren, getNodeAncestors } from '@hi-ui/tree-utils' import { isArrayNonEmpty } from '@hi-ui/type-assertion' /** @@ -16,11 +16,12 @@ export const processCheckedIds = ( allowCheck: (node: any) => boolean ) => { const keySet = new Set(checkedIds) + const flattedDataMap = new Map(flattenData.map((item: any) => [item.id, item])) switch (type) { case 'CHILD': return checkedIds.filter((id) => { - const node = fFindNodeById(flattenData, id) + const node = flattedDataMap.get(id) as any if (node) { const { children } = node @@ -38,7 +39,8 @@ export const processCheckedIds = ( case 'PARENT': return checkedIds.filter((id) => { - const node = fFindNodeById(flattenData, id) as any + const node = flattedDataMap.get(id) as any + if (node) { // 向上递归遍历是否被勾选 const ancestors = getNodeAncestors(node) diff --git a/packages/ui/tag-input/src/TagInputMock.tsx b/packages/ui/tag-input/src/TagInputMock.tsx index df9365ea9..3c2efb5f8 100644 --- a/packages/ui/tag-input/src/TagInputMock.tsx +++ b/packages/ui/tag-input/src/TagInputMock.tsx @@ -56,10 +56,10 @@ export const TagInputMock = forwardRef ) => { const [value, tryChangeValue] = useUncontrolledState(defaultValue, valueProp, onChange) - const tagList = useMemo( - () => value.map((id) => data.find((item) => item.id === id) || { id, title: id }), - [value, data] - ) + const tagList = useMemo(() => { + const dataMap = new Map(data.map((item) => [item.id, item])) + return value.map((id) => dataMap.get(id) || { id, title: id }) + }, [value, data]) const tagCount = tagList.length const [containerWidth = 0, setContainerWidth] = useState() diff --git a/packages/ui/tree/src/hooks/use-check.ts b/packages/ui/tree/src/hooks/use-check.ts index 4cf39e405..2662f5810 100644 --- a/packages/ui/tree/src/hooks/use-check.ts +++ b/packages/ui/tree/src/hooks/use-check.ts @@ -27,8 +27,9 @@ export const useCheck = ( defaultCheckedIds, checkedIdsProp, (checkedIds, checkedNode, checked, semiCheckedIds) => { + const checkedIdsSet = new Set(checkedIds) const nextCheckedNodes = flattedData - .filter((item) => checkedIds.includes(item.id)) + .filter((item) => checkedIdsSet.has(item.id)) .map((item) => item.raw) onCheck?.(checkedIds, { diff --git a/packages/ui/tree/src/utils/index.ts b/packages/ui/tree/src/utils/index.ts index ed72478a4..c156ae73a 100644 --- a/packages/ui/tree/src/utils/index.ts +++ b/packages/ui/tree/src/utils/index.ts @@ -37,17 +37,22 @@ export const processCheckedIds = ( flattenData: any ) => { const keySet = new Set(checkedIds) + const flattedDataMap = new Map(flattenData.map((node: any) => [node.id, node])) switch (type) { case 'CHILD': return checkedIds.filter((id) => { - const node = fFindNodeById(flattenData, id) + const node = flattedDataMap.get(id) as any if (node) { const { children } = node if (isArrayNonEmpty(children)) { - if (children.filter((node) => !node.disabled).every((node) => keySet.has(node.id))) { + if ( + children + .filter((node: any) => !node.disabled) + .every((node: any) => keySet.has(node.id)) + ) { return false } } @@ -59,7 +64,8 @@ export const processCheckedIds = ( case 'PARENT': return checkedIds.filter((id) => { - const node = fFindNodeById(flattenData, id) + const node = flattedDataMap.get(id) as any + if (node) { // 向上递归遍历是否被勾选 const ancestors = getNodeAncestors(node)