Skip to content

Commit 55195ca

Browse files
Kamil Paradowskimeta-codesync[bot]
authored andcommitted
Layout event emitter (#57722)
Summary: > Stack 2/3 — parent: `feat/shadowTreeDidCommit`. Review that first. `onLayout` events are emitted inline from `ShadowTree::emitLayoutEvents`, which forces `ShadowTree` to depend on `ViewProps` and `BaseViewEventEmitter`. This moves that logic into a standalone `LayoutEventEmitter` that consumes the `shadowTreeDidCommit` hook (added in the parent PR) and is registered as a commit hook by `Scheduler`. Same filter (nodes with an `onLayout` prop), same timing (during commit), same `BaseViewEventEmitter::onLayout` call — behavior is unchanged. After this, `ShadowTree` no longer references view props or event emitters, and the per-commit layout-change signal is shared with `ResizeObserver` instead of being duplicated. ## Changelog: [INTERNAL] [CHANGED] - Emit `onLayout` from a `LayoutEventEmitter` commit hook instead of inline in `ShadowTree` Pull Request resolved: #57722 Test Plan: Behavior-preserving refactor. Existing `onLayout` tests pass and `ShadowTree` no longer includes `ViewShadowNode`/`ViewProps`. Verified in rn-tester that `onLayout` still fires on mount and on size changes. Reviewed By: christophpurrer Differential Revision: D114045065 Pulled By: javache fbshipit-source-id: 5b887537ad3e947f3a352fb9dd10b7d124f669df
1 parent 3365667 commit 55195ca

14 files changed

Lines changed: 129 additions & 24 deletions

‎packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp‎

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include <jsinspector-modern/tracing/PerformanceTracerSection.h>
1212
#include <react/debug/react_native_assert.h>
1313
#include <react/renderer/components/root/RootComponentDescriptor.h>
14-
#include <react/renderer/components/view/ViewShadowNode.h>
1514
#include <react/renderer/core/LayoutContext.h>
1615
#include <react/renderer/core/LayoutPrimitives.h>
1716
#include <react/renderer/mounting/ShadowTreeRevision.h>
@@ -466,8 +465,6 @@ CommitStatus ShadowTree::tryCommit(
466465
delegate_.shadowTreeDidCommit(
467466
*this, newRevision.rootShadowNode, affectedLayoutableNodes);
468467

469-
emitLayoutEvents(affectedLayoutableNodes);
470-
471468
if (isReactBranch) {
472469
scheduleReactRevisionPromotion();
473470
} else if (commitMode == CommitMode::Normal) {
@@ -637,25 +634,6 @@ void ShadowTree::commitEmptyTree() const {
637634
{/* default commit options */});
638635
}
639636

640-
void ShadowTree::emitLayoutEvents(
641-
std::vector<const LayoutableShadowNode*>& affectedLayoutableNodes) const {
642-
TraceSection s(
643-
"ShadowTree::emitLayoutEvents",
644-
"affectedLayoutableNodes",
645-
affectedLayoutableNodes.size());
646-
647-
for (const auto* layoutableNode : affectedLayoutableNodes) {
648-
if (auto viewProps =
649-
dynamic_cast<const ViewProps*>(layoutableNode->getProps().get())) {
650-
if (viewProps->onLayout) {
651-
static_cast<const BaseViewEventEmitter&>(
652-
*layoutableNode->getEventEmitter())
653-
.onLayout(layoutableNode->getLayoutMetrics());
654-
}
655-
}
656-
}
657-
}
658-
659637
void ShadowTree::notifyDelegatesOfUpdates() const {
660638
delegate_.shadowTreeDidFinishTransaction(mountingCoordinator_, true);
661639
}

‎packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,6 @@ class ShadowTree final {
161161

162162
void mount(ShadowTreeRevision revision, bool mountSynchronously) const;
163163

164-
void emitLayoutEvents(std::vector<const LayoutableShadowNode *> &affectedLayoutableNodes) const;
165-
166164
void scheduleReactRevisionPromotion() const;
167165

168166
const SurfaceId surfaceId_;

‎packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <react/renderer/mounting/MountingOverrideDelegate.h>
2121
#include <react/renderer/mounting/ShadowViewMutation.h>
2222
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
23+
#include <react/renderer/uimanager/LayoutEventEmitter.h>
2324
#include <react/renderer/uimanager/UIManager.h>
2425
#include <react/renderer/uimanager/UIManagerBinding.h>
2526
#include <mutex>
@@ -148,6 +149,11 @@ Scheduler::Scheduler(
148149

149150
delegate_ = delegate;
150151
commitHooks_ = schedulerToolbox.commitHooks;
152+
153+
// Layout events (`onLayout`) are emitted as a standalone consumer of the
154+
// `shadowTreeDidCommit` commit hook.
155+
commitHooks_.push_back(std::make_shared<LayoutEventEmitter>());
156+
151157
uiManager_ = uiManager;
152158

153159
for (auto& commitHook : commitHooks_) {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "LayoutEventEmitter.h"
9+
10+
#include <cxxreact/TraceSection.h>
11+
#include <react/renderer/components/view/ViewShadowNode.h>
12+
#include <react/renderer/core/LayoutableShadowNode.h>
13+
14+
namespace facebook::react {
15+
16+
void LayoutEventEmitter::shadowTreeDidCommit(
17+
const ShadowTree& /*shadowTree*/,
18+
const RootShadowNode::Shared& /*rootShadowNode*/,
19+
const std::vector<const LayoutableShadowNode*>&
20+
affectedLayoutableNodes) noexcept {
21+
TraceSection s(
22+
"LayoutEventEmitter::shadowTreeDidCommit",
23+
"affectedLayoutableNodes",
24+
affectedLayoutableNodes.size());
25+
26+
for (const auto* layoutableNode : affectedLayoutableNodes) {
27+
if (auto viewProps =
28+
dynamic_cast<const ViewProps*>(layoutableNode->getProps().get())) {
29+
if (viewProps->onLayout) {
30+
static_cast<const BaseViewEventEmitter&>(
31+
*layoutableNode->getEventEmitter())
32+
.onLayout(layoutableNode->getLayoutMetrics());
33+
}
34+
}
35+
}
36+
}
37+
38+
} // namespace facebook::react
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <react/renderer/uimanager/UIManagerCommitHook.h>
11+
12+
namespace facebook::react {
13+
14+
/*
15+
* Emits layout events (`onLayout`) for the nodes whose layout changed in
16+
* each commit, using the `shadowTreeDidCommit` commit hook as the source of
17+
* layout changes (this logic used to be implemented directly in
18+
* `ShadowTree`).
19+
*/
20+
class LayoutEventEmitter final : public UIManagerCommitHook {
21+
public:
22+
void commitHookWasRegistered(const UIManager & /*uiManager*/) noexcept override {}
23+
void commitHookWasUnregistered(const UIManager & /*uiManager*/) noexcept override {}
24+
25+
void shadowTreeDidCommit(
26+
const ShadowTree &shadowTree,
27+
const RootShadowNode::Shared &rootShadowNode,
28+
const std::vector<const LayoutableShadowNode *> &affectedLayoutableNodes) noexcept override;
29+
};
30+
31+
} // namespace facebook::react

‎scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3166,6 +3166,12 @@ class facebook::react::LayoutAnimationStatusDelegate {
31663166
class facebook::react::LayoutConformanceShadowNode : public facebook::react::ConcreteShadowNode<facebook::react::LayoutConformanceShadowNodeComponentName, facebook::react::YogaLayoutableShadowNode, facebook::react::LayoutConformanceProps> {
31673167
}
31683168

3169+
class facebook::react::LayoutEventEmitter : public facebook::react::UIManagerCommitHook {
3170+
public virtual void commitHookWasRegistered(const facebook::react::UIManager&) noexcept override;
3171+
public virtual void commitHookWasUnregistered(const facebook::react::UIManager&) noexcept override;
3172+
public virtual void shadowTreeDidCommit(const facebook::react::ShadowTree& shadowTree, const facebook::react::RootShadowNode::Shared& rootShadowNode, const std::vector<const facebook::react::LayoutableShadowNode*>& affectedLayoutableNodes) noexcept override;
3173+
}
3174+
31693175
class facebook::react::LayoutableShadowNode : public facebook::react::ShadowNode {
31703176
public LayoutableShadowNode(const facebook::react::ShadowNode& sourceShadowNode, const facebook::react::ShadowNodeFragment& fragment);
31713177
public LayoutableShadowNode(const facebook::react::ShadowNodeFragment& fragment, const facebook::react::ShadowNodeFamily::Shared& family, facebook::react::ShadowNodeTraits traits);

‎scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3075,6 +3075,12 @@ class facebook::react::LayoutAnimationStatusDelegate {
30753075
class facebook::react::LayoutConformanceShadowNode : public facebook::react::ConcreteShadowNode<facebook::react::LayoutConformanceShadowNodeComponentName, facebook::react::YogaLayoutableShadowNode, facebook::react::LayoutConformanceProps> {
30763076
}
30773077

3078+
class facebook::react::LayoutEventEmitter : public facebook::react::UIManagerCommitHook {
3079+
public virtual void commitHookWasRegistered(const facebook::react::UIManager&) noexcept override;
3080+
public virtual void commitHookWasUnregistered(const facebook::react::UIManager&) noexcept override;
3081+
public virtual void shadowTreeDidCommit(const facebook::react::ShadowTree& shadowTree, const facebook::react::RootShadowNode::Shared& rootShadowNode, const std::vector<const facebook::react::LayoutableShadowNode*>& affectedLayoutableNodes) noexcept override;
3082+
}
3083+
30783084
class facebook::react::LayoutableShadowNode : public facebook::react::ShadowNode {
30793085
public LayoutableShadowNode(const facebook::react::ShadowNode& sourceShadowNode, const facebook::react::ShadowNodeFragment& fragment);
30803086
public LayoutableShadowNode(const facebook::react::ShadowNodeFragment& fragment, const facebook::react::ShadowNodeFamily::Shared& family, facebook::react::ShadowNodeTraits traits);

‎scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3163,6 +3163,12 @@ class facebook::react::LayoutAnimationStatusDelegate {
31633163
class facebook::react::LayoutConformanceShadowNode : public facebook::react::ConcreteShadowNode<facebook::react::LayoutConformanceShadowNodeComponentName, facebook::react::YogaLayoutableShadowNode, facebook::react::LayoutConformanceProps> {
31643164
}
31653165

3166+
class facebook::react::LayoutEventEmitter : public facebook::react::UIManagerCommitHook {
3167+
public virtual void commitHookWasRegistered(const facebook::react::UIManager&) noexcept override;
3168+
public virtual void commitHookWasUnregistered(const facebook::react::UIManager&) noexcept override;
3169+
public virtual void shadowTreeDidCommit(const facebook::react::ShadowTree& shadowTree, const facebook::react::RootShadowNode::Shared& rootShadowNode, const std::vector<const facebook::react::LayoutableShadowNode*>& affectedLayoutableNodes) noexcept override;
3170+
}
3171+
31663172
class facebook::react::LayoutableShadowNode : public facebook::react::ShadowNode {
31673173
public LayoutableShadowNode(const facebook::react::ShadowNode& sourceShadowNode, const facebook::react::ShadowNodeFragment& fragment);
31683174
public LayoutableShadowNode(const facebook::react::ShadowNodeFragment& fragment, const facebook::react::ShadowNodeFamily::Shared& family, facebook::react::ShadowNodeTraits traits);

‎scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5397,6 +5397,12 @@ class facebook::react::LayoutAnimationStatusDelegate {
53975397
class facebook::react::LayoutConformanceShadowNode : public facebook::react::ConcreteShadowNode<facebook::react::LayoutConformanceShadowNodeComponentName, facebook::react::YogaLayoutableShadowNode, facebook::react::LayoutConformanceProps> {
53985398
}
53995399

5400+
class facebook::react::LayoutEventEmitter : public facebook::react::UIManagerCommitHook {
5401+
public virtual void commitHookWasRegistered(const facebook::react::UIManager&) noexcept override;
5402+
public virtual void commitHookWasUnregistered(const facebook::react::UIManager&) noexcept override;
5403+
public virtual void shadowTreeDidCommit(const facebook::react::ShadowTree& shadowTree, const facebook::react::RootShadowNode::Shared& rootShadowNode, const std::vector<const facebook::react::LayoutableShadowNode*>& affectedLayoutableNodes) noexcept override;
5404+
}
5405+
54005406
class facebook::react::LayoutableShadowNode : public facebook::react::ShadowNode {
54015407
public LayoutableShadowNode(const facebook::react::ShadowNode& sourceShadowNode, const facebook::react::ShadowNodeFragment& fragment);
54025408
public LayoutableShadowNode(const facebook::react::ShadowNodeFragment& fragment, const facebook::react::ShadowNodeFamily::Shared& family, facebook::react::ShadowNodeTraits traits);

‎scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5321,6 +5321,12 @@ class facebook::react::LayoutAnimationStatusDelegate {
53215321
class facebook::react::LayoutConformanceShadowNode : public facebook::react::ConcreteShadowNode<facebook::react::LayoutConformanceShadowNodeComponentName, facebook::react::YogaLayoutableShadowNode, facebook::react::LayoutConformanceProps> {
53225322
}
53235323

5324+
class facebook::react::LayoutEventEmitter : public facebook::react::UIManagerCommitHook {
5325+
public virtual void commitHookWasRegistered(const facebook::react::UIManager&) noexcept override;
5326+
public virtual void commitHookWasUnregistered(const facebook::react::UIManager&) noexcept override;
5327+
public virtual void shadowTreeDidCommit(const facebook::react::ShadowTree& shadowTree, const facebook::react::RootShadowNode::Shared& rootShadowNode, const std::vector<const facebook::react::LayoutableShadowNode*>& affectedLayoutableNodes) noexcept override;
5328+
}
5329+
53245330
class facebook::react::LayoutableShadowNode : public facebook::react::ShadowNode {
53255331
public LayoutableShadowNode(const facebook::react::ShadowNode& sourceShadowNode, const facebook::react::ShadowNodeFragment& fragment);
53265332
public LayoutableShadowNode(const facebook::react::ShadowNodeFragment& fragment, const facebook::react::ShadowNodeFamily::Shared& family, facebook::react::ShadowNodeTraits traits);

0 commit comments

Comments
 (0)