From 2f8aa0f29c062cb1e7444a46ba67edbc1acc1180 Mon Sep 17 00:00:00 2001 From: Oskar Eichler Date: Sun, 30 Aug 2026 11:29:46 +0200 Subject: [PATCH] Continue dispatching mutation observer batches --- .../__tests__/MutationObserver-itest.js | 41 +++++++++++++++++++ .../internals/MutationObserverManager.js | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/packages/react-native/src/private/webapis/mutationobserver/__tests__/MutationObserver-itest.js b/packages/react-native/src/private/webapis/mutationobserver/__tests__/MutationObserver-itest.js index 718f03c374c..e40c8a10bb2 100644 --- a/packages/react-native/src/private/webapis/mutationobserver/__tests__/MutationObserver-itest.js +++ b/packages/react-native/src/private/webapis/mutationobserver/__tests__/MutationObserver-itest.js @@ -23,6 +23,7 @@ import {createRef} from 'react'; import {View} from 'react-native'; import setUpMutationObserver from 'react-native/src/private/setup/setUpMutationObserver'; import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement'; +import * as MutationObserverManager from 'react-native/src/private/webapis/mutationobserver/internals/MutationObserverManager'; declare const MutationObserver: Class; declare const MutationRecord: Class; @@ -658,6 +659,46 @@ describe('MutationObserver', () => { expect([...observer2Records2[0].addedNodes]).toEqual([]); expect([...observer2Records2[0].removedNodes]).toEqual([childNode111]); }); + + it('should dispatch other observers after ignoring disconnected records', () => { + const nodeRef = createRef(); + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + const node = ensureReactNativeElement(nodeRef.current); + const activeCallback = jest.fn(); + const activeObserver = new MutationObserver(activeCallback); + activeObserver.observe(node, {childList: true}); + const disconnectedCallback = jest.fn(); + const disconnectedObserver = new MutationObserver(disconnectedCallback); + disconnectedObserver.observe(node, {childList: true}); + + Fantom.runTask(() => { + root.render( + + + , + ); + + Fantom.scheduleTask(() => { + const disconnectedObserverId = + disconnectedObserver.__getObserverID(); + if (disconnectedObserverId == null) { + throw new Error( + 'Expected the disconnected observer to be registered', + ); + } + MutationObserverManager.unregisterObserver(disconnectedObserverId); + }); + }); + + expect(disconnectedCallback).not.toHaveBeenCalled(); + expect(activeCallback).toHaveBeenCalledTimes(1); + activeObserver.disconnect(); + }); }); describe('multiple observed nodes in the same observer', () => { diff --git a/packages/react-native/src/private/webapis/mutationobserver/internals/MutationObserverManager.js b/packages/react-native/src/private/webapis/mutationobserver/internals/MutationObserverManager.js index 84cc0df6538..9b441712591 100644 --- a/packages/react-native/src/private/webapis/mutationobserver/internals/MutationObserverManager.js +++ b/packages/react-native/src/private/webapis/mutationobserver/internals/MutationObserverManager.js @@ -181,7 +181,7 @@ function doNotifyMutationObservers(): void { if (!registeredObserver) { // This could happen if the observer is disconnected between commit // and mount. In this case, we can just ignore the entries. - return; + continue; } const {observer, callback} = registeredObserver;