Skip to content

Commit be95ec3

Browse files
luisherranzDAreRodzpriethor
authored
iAPI: Fix the logic path that merges plain objects (#68579)
* Fix the logic path that merges plain objects * Add changelog --------- Co-authored-by: DAreRodz <[email protected]> Co-authored-by: luisherranz <[email protected]> Co-authored-by: priethor <[email protected]>
1 parent 98cb5d6 commit be95ec3

File tree

3 files changed

+41
-2
lines changed

3 files changed

+41
-2
lines changed

packages/interactivity/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Bug Fixes
6+
7+
- Fix the logic path that merges plain objects ([#68579](https://github.com/WordPress/gutenberg/pull/68579)).
8+
59
## 6.15.0 (2025-01-02)
610

711
### Enhancements

packages/interactivity/src/proxies/state.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,9 @@ const deepMergeRecursive = (
340340

341341
// Handle nested objects
342342
} else if ( isPlainObject( source[ key ] ) ) {
343-
if ( isNew || ( override && ! isPlainObject( target[ key ] ) ) ) {
343+
const targetValue = Object.getOwnPropertyDescriptor( target, key )
344+
?.value;
345+
if ( isNew || ( override && ! isPlainObject( targetValue ) ) ) {
344346
// Create a new object if the property is new or needs to be overridden
345347
target[ key ] = {};
346348
if ( propSignal ) {
@@ -350,9 +352,10 @@ const deepMergeRecursive = (
350352
proxifyState( ns, target[ key ] as Object )
351353
);
352354
}
355+
deepMergeRecursive( target[ key ], source[ key ], override );
353356
}
354357
// Both target and source are plain objects, merge them recursively
355-
if ( isPlainObject( target[ key ] ) ) {
358+
else if ( isPlainObject( targetValue ) ) {
356359
deepMergeRecursive( target[ key ], source[ key ], override );
357360
}
358361

packages/interactivity/src/proxies/test/deep-merge.ts

+32
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,38 @@ describe( 'Interactivity API', () => {
455455
expect( target.message.fontStyle ).toBeUndefined();
456456
} );
457457

458+
it( 'should not overwrite getters that become objects if `override` is false', () => {
459+
const target: any = proxifyState( 'test', {
460+
get message() {
461+
return 'hello';
462+
},
463+
} );
464+
465+
const getterSpy = jest.spyOn( target, 'message', 'get' );
466+
467+
let message: any;
468+
const spy = jest.fn( () => ( message = target.message ) );
469+
effect( spy );
470+
471+
expect( spy ).toHaveBeenCalledTimes( 1 );
472+
expect( message ).toBe( 'hello' );
473+
474+
deepMerge(
475+
target,
476+
{ message: { content: 'hello', fontStyle: 'italic' } },
477+
false
478+
);
479+
480+
// The effect callback reads `target.message`, so the getter is executed once as well.
481+
expect( spy ).toHaveBeenCalledTimes( 1 );
482+
expect( getterSpy ).toHaveBeenCalledTimes( 1 );
483+
484+
expect( message ).toBe( 'hello' );
485+
expect( target.message ).toBe( 'hello' );
486+
expect( target.message.content ).toBeUndefined();
487+
expect( target.message.fontStyle ).toBeUndefined();
488+
} );
489+
458490
it( 'should keep reactivity of arrays that are initially undefined', () => {
459491
const target: any = proxifyState( 'test', {} );
460492

0 commit comments

Comments
 (0)