Skip to content

Commit b1e5dc7

Browse files
use adopt to register and unregister families
1 parent e4cbd9c commit b1e5dc7

14 files changed

Lines changed: 536 additions & 291 deletions

packages/react-native/ReactCommon/react/renderer/core/ComponentDescriptor.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,15 @@ ComponentDescriptor::ComponentDescriptor(
1616
: eventDispatcher_(parameters.eventDispatcher),
1717
contextContainer_(parameters.contextContainer),
1818
flavor_(parameters.flavor),
19-
rawPropsParser_(std::move(rawPropsParser)) {}
19+
rawPropsParser_(std::move(rawPropsParser)) {
20+
if (contextContainer_) {
21+
auto reg = contextContainer_->find<std::shared_ptr<MediaQueryRegistry>>(
22+
"MediaQueryRegistry");
23+
if (reg.has_value()) {
24+
mediaQueryRegistry_ = reg.value();
25+
}
26+
}
27+
}
2028

2129
const std::shared_ptr<const ContextContainer>&
2230
ComponentDescriptor::getContextContainer() const {

packages/react-native/ReactCommon/react/renderer/core/ComponentDescriptor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <react/renderer/core/EventDispatcher.h>
1111
#include <react/renderer/core/InstanceHandle.h>
12+
#include <react/renderer/core/MediaQueryRegistry.h>
1213
#include <react/renderer/core/Props.h>
1314
#include <react/renderer/core/PropsParserContext.h>
1415
#include <react/renderer/core/RawPropsParser.h>
@@ -127,6 +128,7 @@ class ComponentDescriptor {
127128

128129
EventDispatcher::Weak eventDispatcher_;
129130
std::shared_ptr<const ContextContainer> contextContainer_;
131+
std::shared_ptr<MediaQueryRegistry> mediaQueryRegistry_;
130132
Flavor flavor_;
131133
RawPropsParser rawPropsParser_;
132134

packages/react-native/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,19 @@ class ConcreteComponentDescriptor : public ComponentDescriptor {
189189
protected:
190190
virtual void adopt(ShadowNode &shadowNode) const override
191191
{
192-
// Default implementation does nothing.
193192
react_native_assert(shadowNode.getComponentHandle() == getComponentHandle());
193+
194+
// Register/unregister MQ families so the MediaQueryCommitHook knows
195+
// which nodes to process without tree walks.
196+
if (mediaQueryRegistry_) {
197+
if (shadowNode.getProps()->hasMediaQueries()) {
198+
mediaQueryRegistry_->registerFamily(
199+
shadowNode.getSurfaceId(), shadowNode.getFamilyShared());
200+
} else {
201+
mediaQueryRegistry_->unregisterFamily(
202+
shadowNode.getSurfaceId(), shadowNode.getFamilyShared());
203+
}
204+
}
194205
}
195206
};
196207

packages/react-native/ReactCommon/react/renderer/core/MediaQuery.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ enum class MediaFeature : uint8_t {
1818
MaxWidth,
1919
MinHeight,
2020
MaxHeight,
21-
PrefersColorScheme,
2221
};
2322

2423
struct MediaCondition {
2524
MediaFeature feature{};
2625
Float dimensionValue{0};
27-
ColorScheme colorSchemeValue{ColorScheme::Light};
2826

2927
bool evaluate(const MediaQueryEnvironment& env) const {
3028
switch (feature) {
@@ -36,8 +34,6 @@ struct MediaCondition {
3634
return env.viewportHeight >= dimensionValue;
3735
case MediaFeature::MaxHeight:
3836
return env.viewportHeight <= dimensionValue;
39-
case MediaFeature::PrefersColorScheme:
40-
return env.colorScheme == colorSchemeValue;
4137
}
4238
return false;
4339
}

packages/react-native/ReactCommon/react/renderer/core/MediaQueryEnvironment.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,13 @@
1111

1212
namespace facebook::react {
1313

14-
enum class ColorScheme : uint8_t {
15-
Light = 0,
16-
Dark = 1,
17-
};
18-
1914
/**
2015
* Captures the current environment state used to evaluate CSS media queries.
2116
* Viewport dimensions are in React Native points (logical pixels).
2217
*/
2318
struct MediaQueryEnvironment {
2419
Float viewportWidth{0};
2520
Float viewportHeight{0};
26-
ColorScheme colorScheme{ColorScheme::Light};
27-
28-
bool operator==(const MediaQueryEnvironment& other) const = default;
29-
bool operator!=(const MediaQueryEnvironment& other) const = default;
3021
};
3122

3223
} // namespace facebook::react
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 "MediaQueryRegistry.h"
9+
10+
namespace facebook::react {
11+
12+
void MediaQueryRegistry::registerFamily(
13+
SurfaceId surfaceId,
14+
const std::shared_ptr<const ShadowNodeFamily>& family) {
15+
std::lock_guard lock(mutex_);
16+
families_[surfaceId].insert(family);
17+
}
18+
19+
void MediaQueryRegistry::unregisterFamily(
20+
SurfaceId surfaceId,
21+
const std::shared_ptr<const ShadowNodeFamily>& family) {
22+
std::lock_guard lock(mutex_);
23+
auto it = families_.find(surfaceId);
24+
if (it != families_.end()) {
25+
it->second.erase(family);
26+
}
27+
}
28+
29+
std::unordered_set<std::shared_ptr<const ShadowNodeFamily>>
30+
MediaQueryRegistry::getFamilies(SurfaceId surfaceId) const {
31+
std::lock_guard lock(mutex_);
32+
auto it = families_.find(surfaceId);
33+
if (it != families_.end()) {
34+
return it->second;
35+
}
36+
return {};
37+
}
38+
39+
void MediaQueryRegistry::clearSurface(SurfaceId surfaceId) {
40+
std::lock_guard lock(mutex_);
41+
families_.erase(surfaceId);
42+
}
43+
44+
} // namespace facebook::react
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 <mutex>
11+
#include <unordered_map>
12+
#include <unordered_set>
13+
14+
#include <react/renderer/core/ReactPrimitives.h>
15+
#include <react/renderer/core/ShadowNodeFamily.h>
16+
17+
namespace facebook::react {
18+
19+
/**
20+
* Stores which ShadowNodeFamilies have media queries for each surface.
21+
* Populated by ConcreteComponentDescriptor::adopt(), read by
22+
* MediaQueryCommitHook.
23+
*/
24+
class MediaQueryRegistry {
25+
public:
26+
void registerFamily(
27+
SurfaceId surfaceId,
28+
const std::shared_ptr<const ShadowNodeFamily>& family);
29+
30+
void unregisterFamily(
31+
SurfaceId surfaceId,
32+
const std::shared_ptr<const ShadowNodeFamily>& family);
33+
34+
std::unordered_set<std::shared_ptr<const ShadowNodeFamily>> getFamilies(
35+
SurfaceId surfaceId) const;
36+
37+
void clearSurface(SurfaceId surfaceId);
38+
39+
private:
40+
mutable std::mutex mutex_;
41+
std::unordered_map<
42+
SurfaceId,
43+
std::unordered_set<std::shared_ptr<const ShadowNodeFamily>>>
44+
families_;
45+
};
46+
47+
} // namespace facebook::react

packages/react-native/ReactCommon/react/renderer/css/CSSMediaQuery.h

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,6 @@ inline std::optional<MediaFeature> parseFeatureName(std::string_view name) {
4444
if (caseInsensitiveEqual(name, "max-height")) {
4545
return MediaFeature::MaxHeight;
4646
}
47-
if (caseInsensitiveEqual(name, "prefers-color-scheme")) {
48-
return MediaFeature::PrefersColorScheme;
49-
}
5047
return std::nullopt;
5148
}
5249

@@ -88,24 +85,11 @@ inline std::optional<MediaCondition> parseSingleCondition(
8885
MediaCondition condition{};
8986
condition.feature = feature.value();
9087

91-
if (feature.value() != MediaFeature::PrefersColorScheme) {
92-
if (token.type() != CSSTokenType::Number &&
93-
token.type() != CSSTokenType::Dimension) {
94-
return std::nullopt;
95-
}
96-
condition.dimensionValue = static_cast<Float>(token.numericValue());
97-
} else {
98-
if (token.type() != CSSTokenType::Ident) {
99-
return std::nullopt;
100-
}
101-
if (caseInsensitiveEqual(token.stringValue(), "dark")) {
102-
condition.colorSchemeValue = ColorScheme::Dark;
103-
} else if (caseInsensitiveEqual(token.stringValue(), "light")) {
104-
condition.colorSchemeValue = ColorScheme::Light;
105-
} else {
106-
return std::nullopt;
107-
}
88+
if (token.type() != CSSTokenType::Number &&
89+
token.type() != CSSTokenType::Dimension) {
90+
return std::nullopt;
10891
}
92+
condition.dimensionValue = static_cast<Float>(token.numericValue());
10993

11094
token = nextNonWhitespace(tokenizer);
11195
if (token.type() != CSSTokenType::CloseParen) {
@@ -123,7 +107,8 @@ inline std::optional<MediaCondition> parseSingleCondition(
123107
* Supports:
124108
* (min-width: 768)
125109
* (max-width: 1024px)
126-
* (prefers-color-scheme: dark)
110+
* (min-height: 500)
111+
* (max-height: 800)
127112
* Compound: (min-width: 768) and (max-width: 1024)
128113
*/
129114
inline std::optional<MediaQuery> parseMediaQuery(std::string_view css) {

packages/react-native/ReactCommon/react/renderer/scheduler/MediaQueryCommitHook.cpp

Lines changed: 27 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -11,94 +11,58 @@
1111

1212
namespace facebook::react {
1313

14-
MediaQueryCommitHook::MediaQueryCommitHook(UIManager& uiManager)
15-
: uiManager_(uiManager) {
14+
MediaQueryCommitHook::MediaQueryCommitHook(
15+
UIManager& uiManager,
16+
std::shared_ptr<MediaQueryRegistry> registry)
17+
: registry_(std::move(registry)), uiManager_(uiManager) {
1618
uiManager.registerCommitHook(*this);
1719
}
1820

19-
void MediaQueryCommitHook::setColorScheme(ColorScheme scheme) {
20-
{
21-
std::lock_guard lock(mutex_);
22-
if (colorScheme_ == scheme) {
23-
return;
24-
}
25-
colorScheme_ = scheme;
26-
}
27-
28-
const auto& registry = uiManager_.getShadowTreeRegistry();
29-
registry.enumerate(
30-
[](const ShadowTree& shadowTree, bool& /*stop*/) {
31-
shadowTree.commit(
32-
[](const RootShadowNode& oldRoot) {
33-
return std::make_shared<RootShadowNode>(
34-
oldRoot, ShadowNodeFragment{});
35-
},
36-
{.enableStateReconciliation = false});
37-
});
38-
}
39-
4021
MediaQueryEnvironment MediaQueryCommitHook::buildEnvironment(
4122
const RootShadowNode& rootNode) const {
4223
const auto& rootProps =
4324
static_cast<const RootProps&>(*rootNode.getProps());
44-
45-
std::lock_guard lock(mutex_);
4625
return MediaQueryEnvironment{
4726
.viewportWidth = rootProps.layoutConstraints.maximumSize.width,
4827
.viewportHeight = rootProps.layoutConstraints.maximumSize.height,
49-
.colorScheme = colorScheme_,
5028
};
5129
}
5230

53-
void MediaQueryCommitHook::discoverFamilies(
54-
SurfaceId surfaceId,
55-
const ShadowNode& node,
56-
std::unordered_set<std::shared_ptr<const ShadowNodeFamily>>& families)
57-
const {
58-
if (node.getProps()->hasMediaQueries()) {
59-
families.insert(node.getFamilyShared());
60-
}
61-
for (const auto& child : node.getChildren()) {
62-
discoverFamilies(surfaceId, *child, families);
63-
}
64-
}
65-
6631
RootShadowNode::Unshared MediaQueryCommitHook::shadowTreeWillCommit(
6732
const ShadowTree& shadowTree,
68-
const RootShadowNode::Shared& /*oldRootShadowNode*/,
33+
const RootShadowNode::Shared& oldRootShadowNode,
6934
const RootShadowNode::Unshared& newRootShadowNode,
7035
const ShadowTreeCommitOptions& commitOptions) noexcept {
7136
auto surfaceId = shadowTree.getSurfaceId();
72-
auto env = buildEnvironment(*newRootShadowNode);
7337

74-
// On React commits, re-discover MQ families (nodes may have been
75-
// added, removed, or changed styles).
76-
if (commitOptions.source == ShadowTreeCommitSource::React) {
77-
std::unordered_set<std::shared_ptr<const ShadowNodeFamily>> families;
78-
discoverFamilies(surfaceId, *newRootShadowNode, families);
79-
80-
std::lock_guard lock(mutex_);
81-
if (families.empty()) {
82-
cachedFamilies_.erase(surfaceId);
83-
} else {
84-
cachedFamilies_[surfaceId] = std::move(families);
85-
}
38+
auto families = registry_->getFamilies(surfaceId);
39+
if (families.empty()) {
40+
return newRootShadowNode;
41+
}
42+
43+
// Check if viewport changed by comparing old vs new root.
44+
auto env = buildEnvironment(*newRootShadowNode);
45+
bool viewportChanged = false;
46+
if (oldRootShadowNode) {
47+
auto oldEnv = buildEnvironment(*oldRootShadowNode);
48+
viewportChanged = (oldEnv.viewportWidth != env.viewportWidth ||
49+
oldEnv.viewportHeight != env.viewportHeight);
8650
}
8751

88-
// Get the cached families for this surface.
89-
std::unordered_set<std::shared_ptr<const ShadowNodeFamily>> liveFamilies;
90-
{
91-
std::lock_guard lock(mutex_);
92-
auto it = cachedFamilies_.find(surfaceId);
93-
if (it == cachedFamilies_.end() || it->second.empty()) {
94-
return newRootShadowNode;
95-
}
96-
liveFamilies = it->second;
52+
bool isReactCommit =
53+
commitOptions.source == ShadowTreeCommitSource::React;
54+
55+
// Only run when something MQ-relevant happened:
56+
// - Viewport changed (rotation/resize)
57+
// - React commit (new/changed MQ nodes possible)
58+
// Skip scroll, animation, non-React state updates.
59+
if (!viewportChanged && !isReactCommit) {
60+
return newRootShadowNode;
9761
}
9862

9963
auto clonedRoot = std::static_pointer_cast<RootShadowNode>(
10064
newRootShadowNode->cloneMultiple(
101-
liveFamilies,
65+
families,
10266
[&env](
10367
const ShadowNode& shadowNode,
10468
const ShadowNodeFragment& fragment) {
@@ -112,8 +76,6 @@ RootShadowNode::Unshared MediaQueryCommitHook::shadowTreeWillCommit(
11276

11377
const auto& mqData = props->mediaQueryData_.value();
11478

115-
// Start with base values for all MQ-touched keys,
116-
// then layer matching query overrides on top.
11779
folly::dynamic resolved(mqData.baseStyles);
11880
for (const auto& entry : mqData.queries) {
11981
if (entry.condition.evaluate(env)) {

0 commit comments

Comments
 (0)