From 5c72bb541a7fa099518990495ccd14c5e3b09fdb Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 15 Oct 2024 12:09:47 +0800 Subject: [PATCH 1/8] refactor code --- src/hooks/useCheckedKeys.ts | 19 ++++++++++--------- src/hooks/useFilterTreeData.ts | 16 +++++----------- src/utils/valueUtil.ts | 26 +++++++++++++------------- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/hooks/useCheckedKeys.ts b/src/hooks/useCheckedKeys.ts index d5d1e938..6193b32f 100644 --- a/src/hooks/useCheckedKeys.ts +++ b/src/hooks/useCheckedKeys.ts @@ -10,19 +10,20 @@ export default ( keyEntities: Record, ) => React.useMemo(() => { - let checkedKeys: SafeKey[] = rawLabeledValues.map(({ value }) => value); - let halfCheckedKeys: SafeKey[] = rawHalfCheckedValues.map(({ value }) => value); + const getValues = (values: LabeledValueType[]) => values.map(({ value }) => value); + const checkedKeys = getValues(rawLabeledValues); + const halfCheckedKeys = getValues(rawHalfCheckedValues); const missingValues = checkedKeys.filter(key => !keyEntities[key]); - if (treeConduction) { - ({ checkedKeys, halfCheckedKeys } = conductCheck(checkedKeys, true, keyEntities)); - } + const finalCheckedKeys = treeConduction + ? conductCheck(checkedKeys, true, keyEntities).checkedKeys + : checkedKeys; return [ - // Checked keys should fill with missing keys which should de-duplicated - Array.from(new Set([...missingValues, ...checkedKeys])), - // Half checked keys - halfCheckedKeys, + Array.from(new Set([...missingValues, ...finalCheckedKeys])), + treeConduction + ? conductCheck(checkedKeys, true, keyEntities).halfCheckedKeys + : halfCheckedKeys, ]; }, [rawLabeledValues, rawHalfCheckedValues, treeConduction, keyEntities]); diff --git a/src/hooks/useFilterTreeData.ts b/src/hooks/useFilterTreeData.ts index 64373f10..9a16b19d 100644 --- a/src/hooks/useFilterTreeData.ts +++ b/src/hooks/useFilterTreeData.ts @@ -25,17 +25,11 @@ export default ( return treeData; } - let filterOptionFunc: FilterFn; - if (typeof filterTreeNode === 'function') { - filterOptionFunc = filterTreeNode; - } else { - const upperStr = searchValue.toUpperCase(); - filterOptionFunc = (_, dataNode) => { - const value = dataNode[treeNodeFilterProp]; - - return String(value).toUpperCase().includes(upperStr); - }; - } + const filterOptionFunc: FilterFn = + typeof filterTreeNode === 'function' + ? filterTreeNode + : (_, dataNode) => + String(dataNode[treeNodeFilterProp]).toUpperCase().includes(searchValue.toUpperCase()); function dig(list: DefaultOptionType[], keepAll: boolean = false) { return list.reduce((total, dataNode) => { diff --git a/src/utils/valueUtil.ts b/src/utils/valueUtil.ts index 220a0108..9e940a55 100644 --- a/src/utils/valueUtil.ts +++ b/src/utils/valueUtil.ts @@ -25,22 +25,22 @@ export function isCheckDisabled(node: DataNode) { return !node || node.disabled || node.disableCheckbox || node.checkable === false; } -/** Loop fetch all the keys exist in the tree */ -export function getAllKeys(treeData: DefaultOptionType[], fieldNames: InternalFieldName) { +/** 递归获取树中所有存在的键 */ +export function getAllKeys( + treeData: DefaultOptionType[], + fieldNames: InternalFieldName, +): SafeKey[] { const keys: SafeKey[] = []; - - function dig(list: DefaultOptionType[]) { - list.forEach(item => { - const children = item[fieldNames.children]; - if (children) { - keys.push(item[fieldNames.value]); - dig(children); + const traverseTree = (nodes: DefaultOptionType[]): void => { + nodes.forEach(node => { + keys.push(node[fieldNames.value]); + const children = node[fieldNames.children]; + if (Array.isArray(children)) { + traverseTree(children); } }); - } - - dig(treeData); - + }; + traverseTree(treeData); return keys; } From 02f25e5e9c86d7a7179c6d3066320c8ddc2a7aa0 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 15 Oct 2024 12:12:28 +0800 Subject: [PATCH 2/8] refactor code --- src/utils/strategyUtil.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/utils/strategyUtil.ts b/src/utils/strategyUtil.ts index bf1b3523..5258a5fa 100644 --- a/src/utils/strategyUtil.ts +++ b/src/utils/strategyUtil.ts @@ -17,8 +17,8 @@ export function formatStrategyValues( ): SafeKey[] { const valueSet = new Set(values); - if (strategy === SHOW_CHILD) { - return values.filter(key => { + const strategyHandlers = { + [SHOW_CHILD]: (key: SafeKey) => { const entity = keyEntities[key]; return ( !entity || @@ -28,14 +28,14 @@ export function formatStrategyValues( ({ node }) => isCheckDisabled(node) || valueSet.has(node[fieldNames.value]), ) ); - }); - } - if (strategy === SHOW_PARENT) { - return values.filter(key => { + }, + [SHOW_PARENT]: (key: SafeKey) => { const entity = keyEntities[key]; - const parent = entity ? entity.parent : null; + const parent = entity?.parent; return !parent || isCheckDisabled(parent.node) || !valueSet.has(parent.key); - }); - } - return values; + }, + [SHOW_ALL]: () => true, + }; + + return values.filter(strategyHandlers[strategy] || strategyHandlers[SHOW_ALL]); } From 92d47ce240508ca83267995b3f9b34b440450100 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 15 Oct 2024 12:13:49 +0800 Subject: [PATCH 3/8] refactor code --- src/hooks/useFilterTreeData.ts | 38 ++++++++++----------- src/hooks/useTreeData.ts | 60 +++++++++++++++------------------- src/utils/valueUtil.ts | 7 ++-- 3 files changed, 45 insertions(+), 60 deletions(-) diff --git a/src/hooks/useFilterTreeData.ts b/src/hooks/useFilterTreeData.ts index 9a16b19d..2f764b30 100644 --- a/src/hooks/useFilterTreeData.ts +++ b/src/hooks/useFilterTreeData.ts @@ -2,22 +2,18 @@ import * as React from 'react'; import type { DefaultOptionType, InternalFieldName, TreeSelectProps } from '../TreeSelect'; import { fillLegacyProps } from '../utils/legacyUtil'; -type GetFuncType = T extends boolean ? never : T; -type FilterFn = GetFuncType; +type FilterFn = NonNullable; -export default ( +const useFilterTreeData = ( treeData: DefaultOptionType[], searchValue: string, - { - treeNodeFilterProp, - filterTreeNode, - fieldNames, - }: { + options: { fieldNames: InternalFieldName; treeNodeFilterProp: string; filterTreeNode: TreeSelectProps['filterTreeNode']; }, ) => { + const { fieldNames, treeNodeFilterProp, filterTreeNode } = options; const { children: fieldChildren } = fieldNames; return React.useMemo(() => { @@ -31,24 +27,24 @@ export default ( : (_, dataNode) => String(dataNode[treeNodeFilterProp]).toUpperCase().includes(searchValue.toUpperCase()); - function dig(list: DefaultOptionType[], keepAll: boolean = false) { - return list.reduce((total, dataNode) => { - const children = dataNode[fieldChildren]; + const filterTreeNodes = (nodes: DefaultOptionType[], keepAll = false): DefaultOptionType[] => + nodes.reduce((filtered, node) => { + const children = node[fieldChildren]; + const isMatch = keepAll || filterOptionFunc(searchValue, fillLegacyProps(node)); + const filteredChildren = filterTreeNodes(children || [], isMatch); - const match = keepAll || filterOptionFunc(searchValue, fillLegacyProps(dataNode)); - const childList = dig(children || [], match); - - if (match || childList.length) { - total.push({ - ...dataNode, + if (isMatch || filteredChildren.length) { + filtered.push({ + ...node, isLeaf: undefined, - [fieldChildren]: childList, + [fieldChildren]: filteredChildren, }); } - return total; + return filtered; }, []); - } - return dig(treeData); + return filterTreeNodes(treeData); }, [treeData, searchValue, fieldChildren, treeNodeFilterProp, filterTreeNode]); }; + +export default useFilterTreeData; diff --git a/src/hooks/useTreeData.ts b/src/hooks/useTreeData.ts index 26ffbebd..96ea50ad 100644 --- a/src/hooks/useTreeData.ts +++ b/src/hooks/useTreeData.ts @@ -3,45 +3,35 @@ import type { DataNode, SimpleModeConfig } from '../interface'; import { convertChildrenToData } from '../utils/legacyUtil'; import type { DefaultOptionType } from '../TreeSelect'; -function parseSimpleTreeData( - treeData: DataNode[], - { id, pId, rootPId }: SimpleModeConfig, -): DataNode[] { - const keyNodes = {}; - const rootNodeList = []; - - // Fill in the map - const nodeList = treeData.map(node => { - const clone = { ...node }; - const key = clone[id]; - keyNodes[key] = clone; - clone.key = clone.key || key; - return clone; +function buildTreeStructure(nodes: DataNode[], config: SimpleModeConfig): DataNode[] { + const { id, pId, rootPId } = config; + const nodeMap = new Map(); + const rootNodes: DataNode[] = []; + + nodes.forEach(node => { + const nodeKey = node[id]; + const clonedNode = { ...node, key: node.key || nodeKey }; + nodeMap.set(nodeKey, clonedNode); }); - // Connect tree - nodeList.forEach(node => { + nodeMap.forEach(node => { const parentKey = node[pId]; - const parent = keyNodes[parentKey]; + const parent = nodeMap.get(parentKey); - // Fill parent if (parent) { parent.children = parent.children || []; parent.children.push(node); - } - - // Fill root tree node - if (parentKey === rootPId || (!parent && rootPId === null)) { - rootNodeList.push(node); + } else if (parentKey === rootPId || rootPId === null) { + rootNodes.push(node); } }); - return rootNodeList; + return rootNodes; } /** - * Convert `treeData` or `children` into formatted `treeData`. - * Will not re-calculate if `treeData` or `children` not change. + * 将 `treeData` 或 `children` 转换为格式化的 `treeData`。 + * 如果 `treeData` 或 `children` 没有变化,则不会重新计算。 */ export default function useTreeData( treeData: DataNode[], @@ -50,14 +40,16 @@ export default function useTreeData( ): DefaultOptionType[] { return React.useMemo(() => { if (treeData) { - return simpleMode - ? parseSimpleTreeData(treeData, { - id: 'id', - pId: 'pId', - rootPId: null, - ...(simpleMode !== true ? simpleMode : {}), - }) - : treeData; + if (simpleMode) { + const config: SimpleModeConfig = { + id: 'id', + pId: 'pId', + rootPId: null, + ...(typeof simpleMode === 'object' ? simpleMode : {}), + }; + return buildTreeStructure(treeData, config); + } + return treeData; } return convertChildrenToData(children); diff --git a/src/utils/valueUtil.ts b/src/utils/valueUtil.ts index 9e940a55..a66ba045 100644 --- a/src/utils/valueUtil.ts +++ b/src/utils/valueUtil.ts @@ -10,13 +10,10 @@ export function toArray(value: T | T[]): T[] { export function fillFieldNames(fieldNames?: FieldNames) { const { label, value, children } = fieldNames || {}; - - const mergedValue = value || 'value'; - return { _title: label ? [label] : ['title', 'label'], - value: mergedValue, - key: mergedValue, + value: value || 'value', + key: value || 'value', children: children || 'children', }; } From ec9b67e29c3e5e0eff8864eaf64026ae169ecab6 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 15 Oct 2024 12:14:54 +0800 Subject: [PATCH 4/8] refactor code --- src/hooks/useCache.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hooks/useCache.ts b/src/hooks/useCache.ts index ae730e28..b2c550e6 100644 --- a/src/hooks/useCache.ts +++ b/src/hooks/useCache.ts @@ -15,8 +15,9 @@ export default (values: LabeledValueType[]): [LabeledValueType[]] => { const valueLabelsCache = new Map(); const filledValues = values.map(item => { - const { value } = item; - const mergedLabel = item.label ?? valueLabels.get(value); + const { value, label } = item; + const cachedLabel = valueLabels.get(value); + const mergedLabel = label ?? cachedLabel; // Save in cache valueLabelsCache.set(value, mergedLabel); From df7984a49e962f23531f9e502a3fa1fdb05fca0e Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 15 Oct 2024 12:20:13 +0800 Subject: [PATCH 5/8] revert refactor --- src/hooks/useCheckedKeys.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/hooks/useCheckedKeys.ts b/src/hooks/useCheckedKeys.ts index 6193b32f..d5d1e938 100644 --- a/src/hooks/useCheckedKeys.ts +++ b/src/hooks/useCheckedKeys.ts @@ -10,20 +10,19 @@ export default ( keyEntities: Record, ) => React.useMemo(() => { - const getValues = (values: LabeledValueType[]) => values.map(({ value }) => value); - const checkedKeys = getValues(rawLabeledValues); - const halfCheckedKeys = getValues(rawHalfCheckedValues); + let checkedKeys: SafeKey[] = rawLabeledValues.map(({ value }) => value); + let halfCheckedKeys: SafeKey[] = rawHalfCheckedValues.map(({ value }) => value); const missingValues = checkedKeys.filter(key => !keyEntities[key]); - const finalCheckedKeys = treeConduction - ? conductCheck(checkedKeys, true, keyEntities).checkedKeys - : checkedKeys; + if (treeConduction) { + ({ checkedKeys, halfCheckedKeys } = conductCheck(checkedKeys, true, keyEntities)); + } return [ - Array.from(new Set([...missingValues, ...finalCheckedKeys])), - treeConduction - ? conductCheck(checkedKeys, true, keyEntities).halfCheckedKeys - : halfCheckedKeys, + // Checked keys should fill with missing keys which should de-duplicated + Array.from(new Set([...missingValues, ...checkedKeys])), + // Half checked keys + halfCheckedKeys, ]; }, [rawLabeledValues, rawHalfCheckedValues, treeConduction, keyEntities]); From f04e4f7d44486ed90c311560aa42ba674e4dcf20 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 15 Oct 2024 12:21:01 +0800 Subject: [PATCH 6/8] refactor it --- src/hooks/useCheckedKeys.ts | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/hooks/useCheckedKeys.ts b/src/hooks/useCheckedKeys.ts index d5d1e938..64a55ceb 100644 --- a/src/hooks/useCheckedKeys.ts +++ b/src/hooks/useCheckedKeys.ts @@ -3,26 +3,32 @@ import type { DataEntity } from 'rc-tree/lib/interface'; import { conductCheck } from 'rc-tree/lib/utils/conductUtil'; import type { LabeledValueType, SafeKey } from '../interface'; -export default ( +const useCheckedKeys = ( rawLabeledValues: LabeledValueType[], rawHalfCheckedValues: LabeledValueType[], treeConduction: boolean, keyEntities: Record, -) => - React.useMemo(() => { - let checkedKeys: SafeKey[] = rawLabeledValues.map(({ value }) => value); - let halfCheckedKeys: SafeKey[] = rawHalfCheckedValues.map(({ value }) => value); +) => { + return React.useMemo(() => { + const extractValues = (values: LabeledValueType[]): SafeKey[] => + values.map(({ value }) => value); + + const checkedKeys = extractValues(rawLabeledValues); + const halfCheckedKeys = extractValues(rawHalfCheckedValues); const missingValues = checkedKeys.filter(key => !keyEntities[key]); + let finalCheckedKeys = checkedKeys; + let finalHalfCheckedKeys = halfCheckedKeys; + if (treeConduction) { - ({ checkedKeys, halfCheckedKeys } = conductCheck(checkedKeys, true, keyEntities)); + const conductResult = conductCheck(checkedKeys, true, keyEntities); + finalCheckedKeys = conductResult.checkedKeys; + finalHalfCheckedKeys = conductResult.halfCheckedKeys; } - return [ - // Checked keys should fill with missing keys which should de-duplicated - Array.from(new Set([...missingValues, ...checkedKeys])), - // Half checked keys - halfCheckedKeys, - ]; + return [Array.from(new Set([...missingValues, ...finalCheckedKeys])), finalHalfCheckedKeys]; }, [rawLabeledValues, rawHalfCheckedValues, treeConduction, keyEntities]); +}; + +export default useCheckedKeys; From d7f5671b8c211b55093bd3c25dc773afeb99281c Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 15 Oct 2024 12:22:00 +0800 Subject: [PATCH 7/8] revert refactor --- src/hooks/useCache.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hooks/useCache.ts b/src/hooks/useCache.ts index b2c550e6..526b7c54 100644 --- a/src/hooks/useCache.ts +++ b/src/hooks/useCache.ts @@ -16,8 +16,7 @@ export default (values: LabeledValueType[]): [LabeledValueType[]] => { const filledValues = values.map(item => { const { value, label } = item; - const cachedLabel = valueLabels.get(value); - const mergedLabel = label ?? cachedLabel; + const mergedLabel = label ?? valueLabels.get(value); // Save in cache valueLabelsCache.set(value, mergedLabel); From 982e9864b1d3045bfb657eddb075ffac0092b3c5 Mon Sep 17 00:00:00 2001 From: afc163 Date: Tue, 15 Oct 2024 12:26:28 +0800 Subject: [PATCH 8/8] revert refactor --- src/utils/strategyUtil.ts | 20 ++++++++--------- src/utils/valueUtil.ts | 47 +++++++++++++++++---------------------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/utils/strategyUtil.ts b/src/utils/strategyUtil.ts index 5258a5fa..bf1b3523 100644 --- a/src/utils/strategyUtil.ts +++ b/src/utils/strategyUtil.ts @@ -17,8 +17,8 @@ export function formatStrategyValues( ): SafeKey[] { const valueSet = new Set(values); - const strategyHandlers = { - [SHOW_CHILD]: (key: SafeKey) => { + if (strategy === SHOW_CHILD) { + return values.filter(key => { const entity = keyEntities[key]; return ( !entity || @@ -28,14 +28,14 @@ export function formatStrategyValues( ({ node }) => isCheckDisabled(node) || valueSet.has(node[fieldNames.value]), ) ); - }, - [SHOW_PARENT]: (key: SafeKey) => { + }); + } + if (strategy === SHOW_PARENT) { + return values.filter(key => { const entity = keyEntities[key]; - const parent = entity?.parent; + const parent = entity ? entity.parent : null; return !parent || isCheckDisabled(parent.node) || !valueSet.has(parent.key); - }, - [SHOW_ALL]: () => true, - }; - - return values.filter(strategyHandlers[strategy] || strategyHandlers[SHOW_ALL]); + }); + } + return values; } diff --git a/src/utils/valueUtil.ts b/src/utils/valueUtil.ts index a66ba045..047e81e1 100644 --- a/src/utils/valueUtil.ts +++ b/src/utils/valueUtil.ts @@ -1,14 +1,10 @@ import type { DataNode, FieldNames, SafeKey } from '../interface'; import type { DefaultOptionType, InternalFieldName } from '../TreeSelect'; -export function toArray(value: T | T[]): T[] { - if (Array.isArray(value)) { - return value; - } - return value !== undefined ? [value] : []; -} - -export function fillFieldNames(fieldNames?: FieldNames) { +export const toArray = (value: T | T[]): T[] => + Array.isArray(value) ? value : value !== undefined ? [value] : []; + +export const fillFieldNames = (fieldNames?: FieldNames) => { const { label, value, children } = fieldNames || {}; return { _title: label ? [label] : ['title', 'label'], @@ -16,31 +12,30 @@ export function fillFieldNames(fieldNames?: FieldNames) { key: value || 'value', children: children || 'children', }; -} +}; -export function isCheckDisabled(node: DataNode) { - return !node || node.disabled || node.disableCheckbox || node.checkable === false; -} +export const isCheckDisabled = (node: DataNode): boolean => + !node || node.disabled || node.disableCheckbox || node.checkable === false; -/** 递归获取树中所有存在的键 */ -export function getAllKeys( +export const getAllKeys = ( treeData: DefaultOptionType[], fieldNames: InternalFieldName, -): SafeKey[] { +): SafeKey[] => { const keys: SafeKey[] = []; - const traverseTree = (nodes: DefaultOptionType[]): void => { - nodes.forEach(node => { - keys.push(node[fieldNames.value]); - const children = node[fieldNames.children]; - if (Array.isArray(children)) { - traverseTree(children); + + const dig = (list: DefaultOptionType[]): void => { + list.forEach(item => { + const children = item[fieldNames.children]; + if (children) { + keys.push(item[fieldNames.value]); + dig(children); } }); }; - traverseTree(treeData); + + dig(treeData); + return keys; -} +}; -export function isNil(val: any) { - return val === null || val === undefined; -} +export const isNil = (val: any): boolean => val === null || val === undefined;