Skip to content

Commit e325c84

Browse files
committed
fix: Should not deep clone non-standard object with setValues
fix ant-design/ant-design#20206
1 parent 99f04f2 commit e325c84

File tree

2 files changed

+68
-16
lines changed

2 files changed

+68
-16
lines changed

src/utils/valueUtil.ts

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import setIn from 'lodash/fp/set';
22
import get from 'lodash/get';
3-
import { InternalNamePath, NamePath, Store, StoreValue, EventArgs } from '../interface';
3+
import {
4+
InternalNamePath,
5+
NamePath,
6+
Store,
7+
StoreValue,
8+
EventArgs,
9+
} from '../interface';
410
import { toArray } from './typeUtil';
511

612
/**
@@ -19,12 +25,19 @@ export function getValue(store: Store, namePath: InternalNamePath) {
1925
return value;
2026
}
2127

22-
export function setValue(store: Store, namePath: InternalNamePath, value: StoreValue): Store {
28+
export function setValue(
29+
store: Store,
30+
namePath: InternalNamePath,
31+
value: StoreValue,
32+
): Store {
2333
const newStore = setIn(namePath, value, store);
2434
return newStore;
2535
}
2636

27-
export function cloneByNamePathList(store: Store, namePathList: InternalNamePath[]): Store {
37+
export function cloneByNamePathList(
38+
store: Store,
39+
namePathList: InternalNamePath[],
40+
): Store {
2841
let newStore = {};
2942
namePathList.forEach(namePath => {
3043
const value = getValue(store, namePath);
@@ -34,12 +47,21 @@ export function cloneByNamePathList(store: Store, namePathList: InternalNamePath
3447
return newStore;
3548
}
3649

37-
export function containsNamePath(namePathList: InternalNamePath[], namePath: InternalNamePath) {
38-
return namePathList && namePathList.some(path => matchNamePath(path, namePath));
50+
export function containsNamePath(
51+
namePathList: InternalNamePath[],
52+
namePath: InternalNamePath,
53+
) {
54+
return (
55+
namePathList && namePathList.some(path => matchNamePath(path, namePath))
56+
);
3957
}
4058

4159
function isObject(obj: StoreValue) {
42-
return typeof obj === 'object' && obj !== null;
60+
return (
61+
typeof obj === 'object' &&
62+
obj !== null &&
63+
Object.getPrototypeOf(obj) === Object.prototype
64+
);
4365
}
4466

4567
/**
@@ -58,8 +80,10 @@ function internalSetValues<T>(store: T, values: T): T {
5880
const value = values[key];
5981

6082
// If both are object (but target is not array), we use recursion to set deep value
61-
const recursive = isObject(prevValue) && isObject(value) && !Array.isArray(value);
62-
newStore[key] = recursive ? internalSetValues(prevValue, value || {}) : value;
83+
const recursive = isObject(prevValue) && isObject(value);
84+
newStore[key] = recursive
85+
? internalSetValues(prevValue, value || {})
86+
: value;
6387
});
6488

6589
return newStore;
@@ -76,7 +100,11 @@ export function matchNamePath(
76100
namePath: InternalNamePath,
77101
changedNamePath: InternalNamePath | null,
78102
) {
79-
if (!namePath || !changedNamePath || namePath.length !== changedNamePath.length) {
103+
if (
104+
!namePath ||
105+
!changedNamePath ||
106+
namePath.length !== changedNamePath.length
107+
) {
80108
return false;
81109
}
82110
return namePath.every((nameUnit, i) => changedNamePath[i] === nameUnit);
@@ -93,7 +121,12 @@ export function isSimilar(source: SimilarObject, target: SimilarObject) {
93121
return false;
94122
}
95123

96-
if (!source || !target || typeof source !== 'object' || typeof target !== 'object') {
124+
if (
125+
!source ||
126+
!target ||
127+
typeof source !== 'object' ||
128+
typeof target !== 'object'
129+
) {
97130
return false;
98131
}
99132

@@ -105,14 +138,20 @@ export function isSimilar(source: SimilarObject, target: SimilarObject) {
105138
const sourceValue = source[key];
106139
const targetValue = target[key];
107140

108-
if (typeof sourceValue === 'function' && typeof targetValue === 'function') {
141+
if (
142+
typeof sourceValue === 'function' &&
143+
typeof targetValue === 'function'
144+
) {
109145
return true;
110146
}
111147
return sourceValue === targetValue;
112148
});
113149
}
114150

115-
export function defaultGetValueFromEvent(valuePropName: string, ...args: EventArgs) {
151+
export function defaultGetValueFromEvent(
152+
valuePropName: string,
153+
...args: EventArgs
154+
) {
116155
const event = args[0];
117156
if (event && event.target && valuePropName in event.target) {
118157
return (event.target as HTMLInputElement)[valuePropName];
@@ -133,7 +172,12 @@ export function defaultGetValueFromEvent(valuePropName: string, ...args: EventAr
133172
*/
134173
export function move<T>(array: T[], moveIndex: number, toIndex: number) {
135174
const { length } = array;
136-
if (moveIndex < 0 || moveIndex >= length || toIndex < 0 || toIndex >= length) {
175+
if (
176+
moveIndex < 0 ||
177+
moveIndex >= length ||
178+
toIndex < 0 ||
179+
toIndex >= length
180+
) {
137181
return array;
138182
}
139183
const item = array[moveIndex];

tests/utils.test.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,17 @@ describe('utils', () => {
3131
expect(isSimilar(null, {})).toBeFalsy();
3232
});
3333

34-
it('setValues', () => {
35-
expect(setValues({}, { a: 1 }, { b: 2 })).toEqual({ a: 1, b: 2 });
36-
expect(setValues([], [123])).toEqual([123]);
34+
describe('setValues', () => {
35+
it('basic', () => {
36+
expect(setValues({}, { a: 1 }, { b: 2 })).toEqual({ a: 1, b: 2 });
37+
expect(setValues([], [123])).toEqual([123]);
38+
});
39+
40+
it('Correct handle class instance', () => {
41+
const out = setValues({}, { a: 1, b: { c: new Date() } });
42+
expect(out.a).toEqual(1);
43+
expect(out.b.c instanceof Date).toBeTruthy();
44+
});
3745
});
3846
});
3947

0 commit comments

Comments
 (0)