Files
react-native/packages
Rubén Norte b1040c4e46 Implementation of a subset of MutationObserver (#38100)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/38100

This creates an experimental subset of MutationObserver. This will not be available yet and is only compatible with the new React Native architecture. This shouldn't show up in the changelog until we're ready to enable this in some form.

Changelog: [Internal]

## Context

This implements a subset of the `MutationObserver` API (as defined on the Web) for React Native.

This subset allows observing nodes added or removed from the UI tree (at any depth) without modifications in user code. This is not possible at the moment and it's required by some performance logging APIs that rely on observability for tracking.

**This is not intended for general use at the moment**. We will continue experimenting with this API and might change important details about how it works.

## Implementation details

This API is implemented as a native module that registers a commit hook in Fabric. Whenever there's a commit in React Native, we check for nodes added or removed from the shadow tree (compared with the previous committed version).

**This implementation is completely cross-platform.**

This API uses a centralized entity in JS and native to handle registration of observers and dispatch of notifications. The dispatch the notifications for all observers in the same callback so we can easily change the sequencing of events easily (for example, we can change this to use microtasks when they're available in RN).

An important aspect to highlight about this is that we call into JavaScript when executing the commit hooks. **We assume that commit hooks that trigger mutations will always happen in the JS thread**, so it's safe to do so. This was necessary because React nullifies all fields in fiber when they're unmounted, and we're reporting nodes being deleted. If we wait too long to get a reference to the public instances, they become unavailable and this API doesn't work correctly. To avoid this we eagerly get references to these public instances right when mutations are being applied, so it's safe.

## Known limitations

* Only the `childList` and `subtree` options are supported.
* There is a feature flag in React Native to do commits in background threads to avoid having to do that work in JS (including layout). This implementation is NOT compatible with that and we're currently working on removing the background executor permanently.
* Notifications from `MutationObserver` are dispatched as regular JavaScript tasks, not as microtasks, and they don't block rendering/mount/paint. Some consequences of that are:
  * UI changes done in MutationObserver callbacks are not flushed atomically with the changes being observed. This is caused by the lack of microtasks and because Fabric doesn't wait for them to mount commits in the host platform.
  * MutationObserver callbacks cannot reliably set up intersection observers to get a notification for the first paint, as it could have potentially happened already (similar to the previous point).

We are working on infrastructure changes (support for microtasks, blocking paint on microtasks, etc.) to overcome this limitations.

----

Reviewed By: sammy-SC

Differential Revision: D46149086

fbshipit-source-id: be7ba4acd0886d1661a6500d61d2122f34b98b5a
2023-07-03 15:34:34 -07:00
..
2023-07-03 13:24:11 -07:00