Skip to content

Commit f888cf3

Browse files
authored
feat: support custom merge (#711)
* feat: support deep merge * chore: adjust * chore: adjust * chore: adjust * chore: rename * chore: rename
1 parent 4d24a06 commit f888cf3

File tree

3 files changed

+61
-5
lines changed

3 files changed

+61
-5
lines changed

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export { default as useMergedState } from './hooks/useMergedState';
33
export { default as useControlledState } from './hooks/useControlledState';
44
export { supportNodeRef, supportRef, useComposeRef } from './ref';
55
export { default as get } from './utils/get';
6-
export { default as set, merge } from './utils/set';
6+
export { default as set, merge, mergeWith } from './utils/set';
77
export { default as warning, noteOnce } from './warning';
88
export { default as omit } from './omit';
99
export { default as toArray } from './Children/toArray';

src/utils/set.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,25 @@ function createEmpty<T>(source: T) {
6666

6767
const keys = typeof Reflect === 'undefined' ? Object.keys : Reflect.ownKeys;
6868

69+
// ================================ Merge ================================
70+
export type MergeFn = (current: any, next: any) => any;
71+
6972
/**
70-
* Merge objects which will create
73+
* Merge multiple objects. Support custom merge logic.
74+
* @param sources object sources
75+
* @param config.prepareArray Customize array prepare function.
76+
* It will return empty [] by default.
77+
* So when match array, it will auto be override with next array in sources.
7178
*/
72-
export function merge<T extends object>(...sources: T[]) {
79+
export function mergeWith<T extends object>(
80+
sources: T[],
81+
config: {
82+
prepareArray?: MergeFn;
83+
} = {},
84+
) {
85+
const { prepareArray } = config;
86+
const finalPrepareArray: MergeFn = prepareArray || (() => []);
87+
7388
let clone = createEmpty(sources[0]);
7489

7590
sources.forEach(src => {
@@ -89,7 +104,7 @@ export function merge<T extends object>(...sources: T[]) {
89104

90105
if (isArr) {
91106
// Array will always be override
92-
clone = set(clone, path, []);
107+
clone = set(clone, path, finalPrepareArray(originValue, value));
93108
} else if (!originValue || typeof originValue !== 'object') {
94109
// Init container if not exist
95110
clone = set(clone, path, createEmpty(value));
@@ -109,3 +124,11 @@ export function merge<T extends object>(...sources: T[]) {
109124

110125
return clone;
111126
}
127+
128+
/**
129+
* Merge multiple objects into a new single object.
130+
* Arrays will be replaced by default.
131+
*/
132+
export function merge<T extends object>(...sources: T[]) {
133+
return mergeWith(sources);
134+
}

tests/utils.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pickAttrs from '../src/pickAttrs';
22
import get from '../src/utils/get';
3-
import set, { merge } from '../src/utils/set';
3+
import set, { mergeWith, merge } from '../src/utils/set';
44

55
describe('utils', () => {
66
it('get', () => {
@@ -252,6 +252,39 @@ describe('utils', () => {
252252
[symbol]: 1,
253253
});
254254
});
255+
256+
it('customMerge for custom logic', () => {
257+
const src = {
258+
rest: 233,
259+
list: [
260+
{
261+
a: 1,
262+
},
263+
],
264+
};
265+
const tgt = {
266+
list: [
267+
{
268+
b: 1,
269+
},
270+
],
271+
};
272+
273+
const merged = mergeWith<any>([src, tgt], {
274+
prepareArray: current => {
275+
return [...(current || [])];
276+
},
277+
});
278+
expect(merged).toEqual({
279+
rest: 233,
280+
list: [
281+
{
282+
a: 1,
283+
b: 1,
284+
},
285+
],
286+
});
287+
});
255288
});
256289
});
257290

0 commit comments

Comments
 (0)