From 3613eceff80cec2fadd8e042a255f553614ba61b Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Fri, 25 Mar 2022 16:40:06 +0800 Subject: [PATCH] fix: select deep watch options, close #5398 --- components/vc-select/Select.tsx | 12 +++--- .../vc-select/hooks/useFilterOptions.ts | 8 ++-- components/vc-select/hooks/useOptions.ts | 21 ++++++---- components/vc-tree-select/OptionList.tsx | 2 +- components/vc-tree-select/hooks/useCache.ts | 13 ++++-- .../vc-tree-select/hooks/useCheckedKeys.ts | 12 +++--- .../vc-tree-select/hooks/useDataEntities.ts | 12 +++--- .../vc-tree-select/hooks/useFilterTreeData.ts | 24 ++--------- .../vc-tree-select/hooks/useTreeData.ts | 42 +++++++++++-------- components/vc-tree/Tree.tsx | 34 +++++++-------- 10 files changed, 92 insertions(+), 88 deletions(-) diff --git a/components/vc-select/Select.tsx b/components/vc-select/Select.tsx index 796668dcc..0a7a5fd15 100644 --- a/components/vc-select/Select.tsx +++ b/components/vc-select/Select.tsx @@ -42,7 +42,7 @@ import { toArray } from './utils/commonUtil'; import useFilterOptions from './hooks/useFilterOptions'; import useCache from './hooks/useCache'; import type { Key, VueNode } from '../_util/type'; -import { computed, defineComponent, ref, toRef, watchEffect } from 'vue'; +import { computed, defineComponent, ref, shallowRef, toRef, watchEffect } from 'vue'; import type { ExtractPropTypes, PropType } from 'vue'; import PropTypes from '../_util/vue-types'; import { initDefaultProps } from '../_util/props-util'; @@ -314,13 +314,15 @@ export default defineComponent({ }; // Fill tag as option if mode is `tags` - const filledTagOptions = computed(() => { + const filledTagOptions = shallowRef(); + watchEffect(() => { if (props.mode !== 'tags') { - return mergedOptions.value; + filledTagOptions.value = mergedOptions.value; + return; } // >>> Tag mode - const cloneOptions = [...mergedOptions.value]; + const cloneOptions = mergedOptions.value.slice(); // Check if value exist in options (include new patch item) const existOptions = (val: RawValueType) => valueOptions.value.has(val); @@ -336,7 +338,7 @@ export default defineComponent({ } }); - return cloneOptions; + filledTagOptions.value = cloneOptions; }); const filteredOptions = useFilterOptions( diff --git a/components/vc-select/hooks/useFilterOptions.ts b/components/vc-select/hooks/useFilterOptions.ts index 41f52cd4d..8a63581ad 100644 --- a/components/vc-select/hooks/useFilterOptions.ts +++ b/components/vc-select/hooks/useFilterOptions.ts @@ -7,15 +7,15 @@ import type { BaseOptionType, } from '../Select'; import { injectPropsWithOption } from '../utils/valueUtil'; -import type { Ref } from 'vue'; -import { toRaw, computed } from 'vue'; +import type { Ref, ShallowRef } from 'vue'; +import { computed } from 'vue'; function includes(test: any, search: string) { return toArray(test).join('').toUpperCase().includes(search); } export default ( - options: Ref, + options: ShallowRef, fieldNames: Ref, searchValue?: Ref, filterOption?: Ref, @@ -55,7 +55,7 @@ export default ( ? opt => injectPropsWithOption(opt) : opt => opt; - toRaw(options.value).forEach(item => { + options.value.forEach(item => { // Group should check child options if (item[fieldOptions]) { // Check group first diff --git a/components/vc-select/hooks/useOptions.ts b/components/vc-select/hooks/useOptions.ts index 727a70a4c..4518f661f 100644 --- a/components/vc-select/hooks/useOptions.ts +++ b/components/vc-select/hooks/useOptions.ts @@ -1,5 +1,5 @@ import type { Ref } from 'vue'; -import { toRaw, shallowRef, watchEffect } from 'vue'; +import { toRaw, shallowRef, watchEffect, watch } from 'vue'; import type { FieldNames, RawValueType } from '../Select'; import { convertChildrenToData } from '../utils/legacyUtil'; @@ -15,13 +15,20 @@ export default function useOptions( const mergedOptions = shallowRef(); const valueOptions = shallowRef(); const labelOptions = shallowRef(); + const tempMergedOptions = shallowRef([]); + watch( + [options, children], + () => { + if (options.value) { + tempMergedOptions.value = toRaw(options.value).slice(); + } else { + tempMergedOptions.value = convertChildrenToData(children.value); + } + }, + { immediate: true, deep: true }, + ); watchEffect(() => { - let newOptions = toRaw(options.value); - const childrenAsData = !options.value; - - if (childrenAsData) { - newOptions = convertChildrenToData(children.value); - } + const newOptions = tempMergedOptions.value; const newValueOptions = new Map(); const newLabelOptions = new Map(); diff --git a/components/vc-tree-select/OptionList.tsx b/components/vc-tree-select/OptionList.tsx index 86d66c749..fd3bd65a9 100644 --- a/components/vc-tree-select/OptionList.tsx +++ b/components/vc-tree-select/OptionList.tsx @@ -98,7 +98,7 @@ export default defineComponent({ ); const mergedExpandedKeys = computed(() => { if (legacyContext.treeExpandedKeys) { - return toRaw(legacyContext.treeExpandedKeys).slice(); + return legacyContext.treeExpandedKeys.slice(); } return baseProps.searchValue ? searchExpandedKeys.value : expandedKeys.value; }); diff --git a/components/vc-tree-select/hooks/useCache.ts b/components/vc-tree-select/hooks/useCache.ts index 326fd94d9..40465e0fd 100644 --- a/components/vc-tree-select/hooks/useCache.ts +++ b/components/vc-tree-select/hooks/useCache.ts @@ -1,5 +1,5 @@ import type { Ref } from 'vue'; -import { toRaw, computed, shallowRef } from 'vue'; +import { watch, toRaw, computed, shallowRef } from 'vue'; import type { LabeledValueType, RawValueType } from '../TreeSelect'; /** @@ -10,12 +10,19 @@ export default (values: Ref): [Ref] => { const cacheRef = shallowRef({ valueLabels: new Map(), }); - + const mergedValues = shallowRef(); + watch( + values, + () => { + mergedValues.value = toRaw(values.value); + }, + { immediate: true }, + ); const newFilledValues = computed(() => { const { valueLabels } = cacheRef.value; const valueLabelsCache = new Map(); - const filledValues = toRaw(values.value).map(item => { + const filledValues = mergedValues.value.map(item => { const { value } = item; const mergedLabel = item.label ?? valueLabels.get(value); diff --git a/components/vc-tree-select/hooks/useCheckedKeys.ts b/components/vc-tree-select/hooks/useCheckedKeys.ts index 4b9552ad6..985983ac3 100644 --- a/components/vc-tree-select/hooks/useCheckedKeys.ts +++ b/components/vc-tree-select/hooks/useCheckedKeys.ts @@ -3,11 +3,11 @@ import type { DataEntity } from '../../vc-tree/interface'; import { conductCheck } from '../../vc-tree/utils/conductUtil'; import type { LabeledValueType, RawValueType } from '../TreeSelect'; import type { Ref, ShallowRef } from 'vue'; -import { toRaw, shallowRef, watchEffect } from 'vue'; +import { shallowRef, watchEffect } from 'vue'; export default ( - rawLabeledValues: Ref, - rawHalfCheckedValues: Ref, + rawLabeledValues: ShallowRef, + rawHalfCheckedValues: ShallowRef, treeConduction: Ref, keyEntities: Ref>, maxLevel: Ref, @@ -17,10 +17,8 @@ export default ( const newRawHalfCheckedValues = shallowRef([]); watchEffect(() => { - let checkedKeys: RawValueType[] = toRaw(rawLabeledValues.value).map(({ value }) => value); - let halfCheckedKeys: RawValueType[] = toRaw(rawHalfCheckedValues.value).map( - ({ value }) => value, - ); + let checkedKeys: RawValueType[] = rawLabeledValues.value.map(({ value }) => value); + let halfCheckedKeys: RawValueType[] = rawHalfCheckedValues.value.map(({ value }) => value); const missingValues = checkedKeys.filter(key => !keyEntities.value[key]); diff --git a/components/vc-tree-select/hooks/useDataEntities.ts b/components/vc-tree-select/hooks/useDataEntities.ts index 31be826a8..f534482ad 100644 --- a/components/vc-tree-select/hooks/useDataEntities.ts +++ b/components/vc-tree-select/hooks/useDataEntities.ts @@ -3,16 +3,16 @@ import type { DataEntity } from '../../vc-tree/interface'; import type { FieldNames, RawValueType } from '../TreeSelect'; import { isNil } from '../utils/valueUtil'; -import type { Ref } from 'vue'; -import { toRaw, ref, watchEffect } from 'vue'; +import type { Ref, ShallowRef } from 'vue'; +import { shallowRef, watchEffect } from 'vue'; import { warning } from '../../vc-util/warning'; -export default (treeData: Ref, fieldNames: Ref) => { - const valueEntities = ref>(new Map()); - const keyEntities = ref>({}); +export default (treeData: ShallowRef, fieldNames: Ref) => { + const valueEntities = shallowRef>(new Map()); + const keyEntities = shallowRef>({}); watchEffect(() => { const fieldNamesValue = fieldNames.value; - const collection = convertDataToEntities(toRaw(treeData.value), { + const collection = convertDataToEntities(treeData.value, { fieldNames: fieldNamesValue, initWrapper: wrapper => ({ ...wrapper, diff --git a/components/vc-tree-select/hooks/useFilterTreeData.ts b/components/vc-tree-select/hooks/useFilterTreeData.ts index 409b5a643..d63615235 100644 --- a/components/vc-tree-select/hooks/useFilterTreeData.ts +++ b/components/vc-tree-select/hooks/useFilterTreeData.ts @@ -1,5 +1,5 @@ -import type { Ref } from 'vue'; -import { toRaw, computed } from 'vue'; +import type { Ref, ShallowRef } from 'vue'; +import { computed } from 'vue'; import type { DefaultOptionType, InternalFieldName, TreeSelectProps } from '../TreeSelect'; import { fillLegacyProps } from '../utils/legacyUtil'; @@ -7,7 +7,7 @@ type GetFuncType = T extends boolean ? never : T; type FilterFn = GetFuncType; export default ( - treeData: Ref, + treeData: ShallowRef, searchValue: Ref, { treeNodeFilterProp, @@ -56,24 +56,8 @@ export default ( } } return res; - // return list - // .map(dataNode => { - // const children = dataNode[fieldChildren]; - - // const match = keepAll || filterOptionFunc(searchValueVal, fillLegacyProps(dataNode)); - // const childList = dig(children || [], match); - - // if (match || childList.length) { - // return { - // ...dataNode, - // [fieldChildren]: childList, - // }; - // } - // return null; - // }) - // .filter(node => node); } - return dig(toRaw(treeData.value)); + return dig(treeData.value); }); }; diff --git a/components/vc-tree-select/hooks/useTreeData.ts b/components/vc-tree-select/hooks/useTreeData.ts index 4fde7c8a4..d3ddccb5b 100644 --- a/components/vc-tree-select/hooks/useTreeData.ts +++ b/components/vc-tree-select/hooks/useTreeData.ts @@ -1,5 +1,5 @@ -import type { Ref } from 'vue'; -import { toRaw, computed } from 'vue'; +import type { Ref, ShallowRef } from 'vue'; +import { shallowRef, watch, toRaw } from 'vue'; import type { DataNode, SimpleModeConfig } from '../interface'; import { convertChildrenToData } from '../utils/legacyUtil'; import type { DefaultOptionType } from '../TreeSelect'; @@ -49,20 +49,26 @@ export default function useTreeData( treeData: Ref, children: Ref, simpleMode: Ref, -): Ref { - return computed(() => { - const simpleModeValue = simpleMode.value; - if (treeData.value) { - return simpleMode.value - ? parseSimpleTreeData(toRaw(treeData.value), { - id: 'id', - pId: 'pId', - rootPId: null, - ...(simpleModeValue !== true ? simpleModeValue : {}), - }) - : treeData.value; - } - - return convertChildrenToData(toRaw(children.value)); - }); +): ShallowRef { + const mergedTreeData = shallowRef(); + watch( + [simpleMode, treeData, children], + () => { + const simpleModeValue = simpleMode.value; + if (treeData.value) { + mergedTreeData.value = simpleMode.value + ? parseSimpleTreeData(toRaw(treeData.value), { + id: 'id', + pId: 'pId', + rootPId: null, + ...(simpleModeValue !== true ? simpleModeValue : {}), + }) + : toRaw(treeData.value); + } else { + mergedTreeData.value = convertChildrenToData(toRaw(children.value)); + } + }, + { immediate: true, deep: true }, + ); + return mergedTreeData; } diff --git a/components/vc-tree/Tree.tsx b/components/vc-tree/Tree.tsx index 6247508ad..46b58d6da 100644 --- a/components/vc-tree/Tree.tsx +++ b/components/vc-tree/Tree.tsx @@ -104,12 +104,16 @@ export default defineComponent({ dragOverNodeKey: null, }); const treeData = shallowRef([]); - watchEffect(() => { - treeData.value = - props.treeData !== undefined - ? toRaw(props.treeData) - : convertTreeToData(toRaw(props.children)); - }); + watch( + [() => props.treeData, () => props.children], + () => { + treeData.value = + props.treeData !== undefined + ? toRaw(props.treeData) + : convertTreeToData(toRaw(props.children)); + }, + { immediate: true, deep: true }, + ); const keyEntities = shallowRef({}); const focused = ref(false); @@ -143,7 +147,7 @@ export default defineComponent({ watchEffect(() => { if (treeData.value) { - const entitiesMap = convertDataToEntities(toRaw(treeData.value), { + const entitiesMap = convertDataToEntities(treeData.value, { fieldNames: fieldNames.value, }); keyEntities.value = { @@ -190,19 +194,15 @@ export default defineComponent({ // ================ flattenNodes ================= const flattenNodes = shallowRef([]); watchEffect(() => { - flattenNodes.value = flattenTreeData( - toRaw(treeData.value), - toRaw(expandedKeys.value), - fieldNames.value, - ); + flattenNodes.value = flattenTreeData(treeData.value, expandedKeys.value, fieldNames.value); }); // ================ selectedKeys ================= watchEffect(() => { if (props.selectable) { if (props.selectedKeys !== undefined) { - selectedKeys.value = calcSelectedKeys(toRaw(props.selectedKeys), props); + selectedKeys.value = calcSelectedKeys(props.selectedKeys, props); } else if (!init && props.defaultSelectedKeys) { - selectedKeys.value = calcSelectedKeys(toRaw(props.defaultSelectedKeys), props); + selectedKeys.value = calcSelectedKeys(props.defaultSelectedKeys, props); } } }); @@ -213,12 +213,12 @@ export default defineComponent({ let checkedKeyEntity; if (props.checkedKeys !== undefined) { - checkedKeyEntity = parseCheckedKeys(toRaw(props.checkedKeys)) || {}; + checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || {}; } else if (!init && props.defaultCheckedKeys) { - checkedKeyEntity = parseCheckedKeys(toRaw(props.defaultCheckedKeys)) || {}; + checkedKeyEntity = parseCheckedKeys(props.defaultCheckedKeys) || {}; } else if (treeData.value) { // If `treeData` changed, we also need check it - checkedKeyEntity = parseCheckedKeys(toRaw(props.checkedKeys)) || { + checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || { checkedKeys: checkedKeys.value, halfCheckedKeys: halfCheckedKeys.value, };