diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp index 522ec5739c9336..fc591f13ff3bb0 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -289,7 +289,7 @@ CommitStatus ShadowTree::tryCommit( // Run commit hooks. newRootShadowNode = delegate_.shadowTreeWillCommit( - *this, oldRootShadowNode, newRootShadowNode); + *this, oldRootShadowNode, newRootShadowNode, commitOptions); if (!newRootShadowNode) { return CommitStatus::Cancelled; diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h index ae9d005061e7b0..d33dbe3b40c57a 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h @@ -25,6 +25,49 @@ namespace facebook::react { using ShadowTreeCommitTransaction = std::function; +/* + * Represents a result of a `commit` operation. + */ +enum class ShadowTreeCommitStatus { + Succeeded, + Failed, + Cancelled, +}; + +/* + * Represents commits' side-effects propagation mode. + */ +enum class ShadowTreeCommitMode { + // Commits' side-effects are observable via `MountingCoordinator`. + // The rendering pipeline fully works end-to-end. + Normal, + + // Commits' side-effects are *not* observable via `MountingCoordinator`. + // The mounting phase is skipped in the rendering pipeline. + Suspended, +}; + +enum class ShadowTreeCommitSource { + Unknown, + React, +}; + +struct ShadowTreeCommitOptions { + // When set to true, Shadow Node state from current revision will be applied + // to the new revision. For more details see + // https://reactnative.dev/architecture/render-pipeline#react-native-renderer-state-updates + bool enableStateReconciliation{false}; + + // Indicates if mounting will be triggered synchronously and React will + // not get a chance to interrupt painting. + // This should be set to `false` when a commit is coming from React. It + // will then let React run layout effects and apply updates before paint. + // For all other commits, should be true. + bool mountSynchronously{true}; + + ShadowTreeCommitSource source{ShadowTreeCommitSource::Unknown}; +}; + /* * Represents the shadow tree and its lifecycle. */ @@ -32,41 +75,10 @@ class ShadowTree final { public: using Unique = std::unique_ptr; - /* - * Represents a result of a `commit` operation. - */ - enum class CommitStatus { - Succeeded, - Failed, - Cancelled, - }; - - /* - * Represents commits' side-effects propagation mode. - */ - enum class CommitMode { - // Commits' side-effects are observable via `MountingCoordinator`. - // The rendering pipeline fully works end-to-end. - Normal, - - // Commits' side-effects are *not* observable via `MountingCoordinator`. - // The mounting phase is skipped in the rendering pipeline. - Suspended, - }; - - struct CommitOptions { - // When set to true, Shadow Node state from current revision will be applied - // to the new revision. For more details see - // https://reactnative.dev/architecture/render-pipeline#react-native-renderer-state-updates - bool enableStateReconciliation{false}; - - // Indicates if mounting will be triggered synchronously and React will - // not get a chance to interrupt painting. - // This should be set to `false` when a commit is coming from React. It - // will then let React run layout effects and apply updates before paint. - // For all other commits, should be true. - bool mountSynchronously{true}; - }; + using CommitStatus = ShadowTreeCommitStatus; + using CommitMode = ShadowTreeCommitMode; + using CommitSource = ShadowTreeCommitSource; + using CommitOptions = ShadowTreeCommitOptions; /* * Creates a new shadow tree instance. diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h index dbb0fecfc92c2e..14402722045ec0 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h @@ -12,6 +12,7 @@ namespace facebook::react { class ShadowTree; +struct ShadowTreeCommitOptions; /* * Abstract class for ShadowTree's delegate. @@ -27,7 +28,8 @@ class ShadowTreeDelegate { virtual RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) const = 0; + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTreeCommitOptions& commitOptions) const = 0; /* * Called right after Shadow Tree commit a new state of the tree. diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp index 0a685ede067cd8..5dc71f7bf03370 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp @@ -29,7 +29,8 @@ class DummyShadowTreeDelegate : public ShadowTreeDelegate { RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& /*shadowTree*/, const RootShadowNode::Shared& /*oldRootShadowNode*/, - const RootShadowNode::Unshared& newRootShadowNode) const override { + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& /*commitOptions*/) const override { return newRootShadowNode; }; diff --git a/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.cpp b/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.cpp index afcbbe3c0d07d1..7db7a25764072a 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.cpp @@ -106,8 +106,11 @@ void MutationObserverManager::commitHookWasUnregistered( RootShadowNode::Unshared MutationObserverManager::shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) noexcept { - runMutationObservations(shadowTree, *oldRootShadowNode, *newRootShadowNode); + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& commitOptions) noexcept { + if (commitOptions.source == ShadowTree::CommitSource::React) { + runMutationObservations(shadowTree, *oldRootShadowNode, *newRootShadowNode); + } return newRootShadowNode; } diff --git a/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.h b/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.h index 60c53116c81e5c..ffaf3496751c0a 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.h +++ b/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -43,7 +44,8 @@ class MutationObserverManager final : public UIManagerCommitHook { RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) noexcept override; + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& commitOptions) noexcept override; private: std::unordered_map< diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp index 4bcc9c434721d1..bf5323bd2113f4 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -619,7 +619,8 @@ void UIManager::unregisterMountHook(UIManagerMountHook& mountHook) { RootShadowNode::Unshared UIManager::shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) const { + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& commitOptions) const { TraceSection s("UIManager::shadowTreeWillCommit"); std::shared_lock lock(commitHookMutex_); @@ -627,7 +628,7 @@ RootShadowNode::Unshared UIManager::shadowTreeWillCommit( auto resultRootShadowNode = newRootShadowNode; for (auto* commitHook : commitHooks_) { resultRootShadowNode = commitHook->shadowTreeWillCommit( - shadowTree, oldRootShadowNode, resultRootShadowNode); + shadowTree, oldRootShadowNode, resultRootShadowNode, commitOptions); } return resultRootShadowNode; diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h index 6218c1c2fed809..c7ce8a0f0bbd6e 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h @@ -127,7 +127,8 @@ class UIManager final : public ShadowTreeDelegate { RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) const override; + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& commitOptions) const override; std::shared_ptr createNode( Tag tag, diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index e43f94b645ed8a..dddcd52e2de4c1 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -454,7 +454,9 @@ jsi::Value UIManagerBinding::get( uiManager->completeSurface( surfaceId, shadowNodeList, - {.enableStateReconciliation = true, .mountSynchronously = false}); + {.enableStateReconciliation = true, + .mountSynchronously = false, + .source = ShadowTree::CommitSource::React}); return jsi::Value::undefined(); }); diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h index 1667aaf00ee61c..2e15c6ca89e595 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h @@ -12,6 +12,7 @@ namespace facebook::react { class ShadowTree; +struct ShadowTreeCommitOptions; class UIManager; /* @@ -34,7 +35,24 @@ class UIManagerCommitHook { virtual RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) noexcept = 0; + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTreeCommitOptions& /*commitOptions*/) noexcept { + return shadowTreeWillCommit( + shadowTree, oldRootShadowNode, newRootShadowNode); + } + + /* + * This is a version of `shadowTreeWillCommit` without `commitOptions` for + * backward compatibility. + */ + virtual RootShadowNode::Unshared shadowTreeWillCommit( + const ShadowTree& /*shadowTree*/, + const RootShadowNode::Shared& /*oldRootShadowNode*/, + const RootShadowNode::Unshared& newRootShadowNode) noexcept { + // No longer a pure method as subclasses are expected to implement the other + // flavor instead. + return newRootShadowNode; + } virtual ~UIManagerCommitHook() noexcept = default; }; diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp index ae6312baa09cdf..af786dfdb8a596 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp @@ -19,7 +19,8 @@ class FakeShadowTreeDelegate : public ShadowTreeDelegate { RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& /*shadowTree*/, const RootShadowNode::Shared& /*oldRootShadowNode*/, - const RootShadowNode::Unshared& newRootShadowNode) const override { + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& /*commitOptions*/) const override { return newRootShadowNode; };