@@ -337,6 +337,39 @@ describe('google Maps Regressions', () => {
337337 map . setOptions ( rest )
338338 }
339339
340+ function isPlainObject ( value : unknown ) : value is Record < string , unknown > {
341+ if ( ! value || typeof value !== 'object' )
342+ return false
343+ const proto = Object . getPrototypeOf ( value )
344+ return proto === Object . prototype || proto === null
345+ }
346+
347+ function isSameOptionValue ( a : unknown , b : unknown ) : boolean {
348+ if ( Object . is ( a , b ) )
349+ return true
350+ if ( Array . isArray ( a ) && Array . isArray ( b ) )
351+ return a . length === b . length && a . every ( ( value , index ) => isSameOptionValue ( value , b [ index ] ) )
352+ if ( isPlainObject ( a ) && isPlainObject ( b ) ) {
353+ const aKeys = Object . keys ( a )
354+ const bKeys = Object . keys ( b )
355+ return aKeys . length === bKeys . length
356+ && aKeys . every ( key => Object . hasOwn ( b , key ) && isSameOptionValue ( a [ key ] , b [ key ] ) )
357+ }
358+ return false
359+ }
360+
361+ function applyOptionsWithStableGuard (
362+ map : ReturnType < typeof createMockMap > ,
363+ options : Record < string , any > ,
364+ previousOptions : Record < string , any > ,
365+ ) {
366+ const { center : _ , zoom : __ , mapId : ___ , colorScheme : ____ , ...next } = options
367+ const { center : _previousCenter , zoom : _previousZoom , mapId : _previousMapId , colorScheme : _previousColorScheme , ...previous } = previousOptions
368+ if ( isSameOptionValue ( next , previous ) )
369+ return
370+ map . setOptions ( next )
371+ }
372+
340373 it ( 'old behavior: setOptions resets zoom and center on unrelated re-render' , ( ) => {
341374 const map = createMockMap ( )
342375 const options = { center : { lat : 40 , lng : - 74 } , zoom : 12 , mapId : 'abc' }
@@ -402,6 +435,35 @@ describe('google Maps Regressions', () => {
402435 expect ( map . setCenter ) . not . toHaveBeenCalled ( )
403436 expect ( map . setZoom ) . not . toHaveBeenCalled ( )
404437 } )
438+
439+ it ( 'fixed behavior: identical inline options do not call setOptions on unrelated re-renders' , ( ) => {
440+ const map = createMockMap ( )
441+ const baseOptions = {
442+ center : { lat : 40 , lng : - 74 } ,
443+ zoom : 12 ,
444+ mapId : 'abc' ,
445+ disableDefaultUI : true ,
446+ restriction : { strictBounds : false } ,
447+ }
448+
449+ for ( let i = 0 ; i < 3 ; i ++ ) {
450+ applyOptionsWithStableGuard ( map , { ...baseOptions , restriction : { strictBounds : false } } , baseOptions )
451+ }
452+
453+ expect ( map . setOptions ) . not . toHaveBeenCalled ( )
454+ expect ( map . setCenter ) . not . toHaveBeenCalled ( )
455+ expect ( map . setZoom ) . not . toHaveBeenCalled ( )
456+ } )
457+
458+ it ( 'fixed behavior: real non-position option changes still call setOptions' , ( ) => {
459+ const map = createMockMap ( )
460+ const previousOptions = { center : { lat : 40 , lng : - 74 } , zoom : 12 , mapId : 'abc' , disableDefaultUI : true }
461+ const nextOptions = { center : { lat : 40 , lng : - 74 } , zoom : 12 , mapId : 'abc' , disableDefaultUI : false }
462+
463+ applyOptionsWithStableGuard ( map , nextOptions , previousOptions )
464+
465+ expect ( map . setOptions ) . toHaveBeenCalledWith ( { disableDefaultUI : false } )
466+ } )
405467 } )
406468
407469 describe ( 'center watcher should skip setCenter when lat/lng unchanged' , ( ) => {
@@ -476,6 +538,40 @@ describe('google Maps Regressions', () => {
476538 expect ( getCenterWatchKey ( firstRender . center ) ) . toBe ( getCenterWatchKey ( secondRender . center ) )
477539 } )
478540
541+ it ( 'keeps the controlled center watch key stable across unrelated inline option re-renders' , ( ) => {
542+ function getCenterWatchKey ( center : any ) {
543+ if ( ! center )
544+ return undefined
545+ const lat = typeof center . lat === 'function' ? center . lat ( ) : center . lat
546+ const lng = typeof center . lng === 'function' ? center . lng ( ) : center . lng
547+ return `latlng:${ lat } ,${ lng } `
548+ }
549+
550+ function getControlledCenterKey ( props : { center ?: any , mapOptions ?: { center ?: any } } , centerOverride ?: any ) {
551+ return getCenterWatchKey ( centerOverride )
552+ || getCenterWatchKey ( props . mapOptions ?. center )
553+ || getCenterWatchKey ( props . center )
554+ }
555+
556+ const firstRender = {
557+ mapOptions : {
558+ center : { lat : - 34.397 , lng : 150.644 } ,
559+ zoom : 8 ,
560+ } ,
561+ }
562+ const secondRenderAfterRectangleToggle = {
563+ mapOptions : {
564+ center : { lat : - 34.397 , lng : 150.644 } ,
565+ zoom : 8 ,
566+ } ,
567+ }
568+
569+ expect ( firstRender . mapOptions ) . not . toBe ( secondRenderAfterRectangleToggle . mapOptions )
570+ expect ( firstRender . mapOptions . center ) . not . toBe ( secondRenderAfterRectangleToggle . mapOptions . center )
571+ expect ( getControlledCenterKey ( firstRender ) )
572+ . toBe ( getControlledCenterKey ( secondRenderAfterRectangleToggle ) )
573+ } )
574+
479575 it ( 'changes the center watch key when coordinates actually change' , ( ) => {
480576 function getCenterWatchKey ( center : any ) {
481577 const lat = typeof center . lat === 'function' ? center . lat ( ) : center . lat
0 commit comments