From 5c4a3cb27b8d1e647c2eab22b4d75b0955c4ee50 Mon Sep 17 00:00:00 2001 From: Simona <36978416+SimonaliaChen@users.noreply.github.com> Date: Fri, 25 Jun 2021 16:08:00 +0800 Subject: [PATCH] fix(cascader-panel): should not re-init when props's value not change (#2344) * fix(cascader-panel): should not re-init when props's value not change fix #2317 #1871 * style: extra space --- .../__tests__/cascader-panel.spec.ts | 36 +++++++++++++++++++ packages/cascader-panel/src/index.vue | 7 +++- packages/utils/util.ts | 19 +++++++++- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/packages/cascader-panel/__tests__/cascader-panel.spec.ts b/packages/cascader-panel/__tests__/cascader-panel.spec.ts index 2a61fc638b..db7746162b 100644 --- a/packages/cascader-panel/__tests__/cascader-panel.spec.ts +++ b/packages/cascader-panel/__tests__/cascader-panel.spec.ts @@ -686,4 +686,40 @@ describe('CascaderPanel.vue', () => { await wrapper.setProps({ options: NORMAL_OPTIONS }) expect(vm.getCheckedNodes(true).length).toBe(1) }) + + test('should not re-init when props\'s reference change but value not change', async () => { + const mockLazyLoad = jest.fn() + const wrapper = _mount({ + template: ` + + `, + data() { + return { + value: [], + props: { + lazy: true, + lazyLoad: mockLazyLoad, + }, + } + }, + }) + + await nextTick() + expect(mockLazyLoad).toBeCalled() + + const sameMockLazyLoad = jest.fn() + wrapper.vm.props.lazyLoad = sameMockLazyLoad + await nextTick() + expect(sameMockLazyLoad).not.toBeCalled() + + // should re-init when props's value change + const differentMockLazyLoad = jest.fn(lazyLoad) + wrapper.vm.props.lazyLoad = differentMockLazyLoad + await nextTick() + expect(differentMockLazyLoad).toBeCalled() + }) + }) diff --git a/packages/cascader-panel/src/index.vue b/packages/cascader-panel/src/index.vue index b451ca099a..5c06d85e44 100644 --- a/packages/cascader-panel/src/index.vue +++ b/packages/cascader-panel/src/index.vue @@ -36,6 +36,7 @@ import { coerceTruthyValueToArray, deduplicate, isEmpty, + isEqualWithFunction, } from '@element-plus/utils/util' import { CommonProps, useCascaderConfig } from './config' import { @@ -295,7 +296,11 @@ export default defineComponent({ watch( [ config, () => props.options ], - initStore, + ([newConfig, newOptions], [oldConfig, oldOptions]) => { + if (isEqualWithFunction(newConfig, oldConfig) && isEqual(newOptions, oldOptions)) return + + initStore() + }, { deep: true, immediate: true }, ) diff --git a/packages/utils/util.ts b/packages/utils/util.ts index ae604af069..3284a30aeb 100644 --- a/packages/utils/util.ts +++ b/packages/utils/util.ts @@ -1,7 +1,9 @@ import type { Ref } from 'vue' import { getCurrentInstance } from 'vue' -import { camelize, capitalize, extend, hasOwn, hyphenate, isArray, isObject, isString, looseEqual, toRawType } from '@vue/shared' +import { camelize, capitalize, extend, hasOwn, hyphenate, isArray, isObject, isString, isFunction, looseEqual, toRawType } from '@vue/shared' + +import isEqualWith from 'lodash/isEqualWith' import isServer from './isServer' import type { AnyFunction } from './types' @@ -236,3 +238,18 @@ export function addUnit(value: string | number) { } return '' } + +/** + * Enhance `lodash.isEqual` for it always return false even two functions have completely same statements. + * @param obj The value to compare + * @param other The other value to compare + * @returns Returns `true` if the values are equivalent, else `false`. + * @example + * lodash.isEqual(() => 1, () => 1) // false + * isEqualWith(() => 1, () => 1) // true + */ +export function isEqualWithFunction (obj: any, other: any) { + return isEqualWith(obj, other, (objVal, otherVal) => { + return isFunction(objVal) && isFunction(otherVal) ? `${objVal}` === `${otherVal}` : undefined + }) +}