react-native 0.84.0-nightly-20251205-95cc1e767 → 0.84.0-nightly-20251206-63b0aef13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/Libraries/Animated/nodes/AnimatedProps.js +29 -3
  2. package/Libraries/Core/ReactNativeVersion.js +1 -1
  3. package/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm +7 -0
  4. package/Libraries/NativeAnimation/React-RCTAnimation.podspec +1 -0
  5. package/React/Base/RCTVersion.m +1 -1
  6. package/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm +7 -0
  7. package/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h +2 -0
  8. package/React/FBReactNativeSpec/FBReactNativeSpecJSI.h +10 -0
  9. package/ReactAndroid/gradle.properties +1 -1
  10. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
  11. package/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +1 -10
  12. package/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewAccessibilityDelegate.kt +1 -0
  13. package/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt +42 -28
  14. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  15. package/ReactCommon/jsi/jsi/jsi.cpp +2 -1
  16. package/ReactCommon/jsinspector-modern/HostAgent.cpp +2 -2
  17. package/ReactCommon/jsinspector-modern/InspectorInterfaces.cpp +10 -5
  18. package/ReactCommon/jsinspector-modern/InspectorInterfaces.h +2 -2
  19. package/ReactCommon/jsinspector-modern/TracingAgent.cpp +1 -1
  20. package/ReactCommon/react/renderer/animated/AnimatedModule.cpp +19 -1
  21. package/ReactCommon/react/renderer/animated/AnimatedModule.h +8 -0
  22. package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.cpp +83 -9
  23. package/ReactCommon/react/renderer/animated/NativeAnimatedNodesManager.h +6 -0
  24. package/ReactCommon/react/renderer/animated/drivers/FrameAnimationDriver.cpp +1 -0
  25. package/ReactCommon/react/renderer/animated/nodes/ColorAnimatedNode.cpp +0 -1
  26. package/ReactCommon/react/renderer/animated/nodes/PropsAnimatedNode.h +1 -0
  27. package/ReactCommon/react/renderer/animated/nodes/RoundAnimatedNode.cpp +1 -1
  28. package/ReactCommon/react/renderer/animated/nodes/ValueAnimatedNode.cpp +1 -1
  29. package/ReactCommon/react/renderer/animated/tests/AnimatedNodeTests.cpp +67 -0
  30. package/ReactCommon/react/renderer/animated/tests/AnimationDriverTests.cpp +59 -0
  31. package/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.cpp +81 -0
  32. package/ReactCommon/react/renderer/animationbackend/AnimatedPropsRegistry.h +83 -0
  33. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.cpp +26 -15
  34. package/ReactCommon/react/renderer/animationbackend/AnimationBackend.h +7 -4
  35. package/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.cpp +69 -0
  36. package/ReactCommon/react/renderer/animationbackend/AnimationBackendCommitHook.h +32 -0
  37. package/ReactCommon/react/renderer/uimanager/UIManager.cpp +6 -0
  38. package/ReactCommon/react/renderer/uimanager/UIManagerAnimationBackend.h +1 -0
  39. package/package.json +9 -9
  40. package/sdks/hermes-engine/version.properties +1 -1
  41. package/src/private/animated/NativeAnimatedHelper.js +29 -1
  42. package/src/private/specs_DEPRECATED/modules/NativeAnimatedModule.js +4 -0
@@ -21,6 +21,7 @@
21
21
  #include <react/renderer/animationbackend/AnimationBackend.h>
22
22
  #endif
23
23
  #include <react/renderer/core/ReactPrimitives.h>
24
+ #include <react/renderer/core/ShadowNode.h>
24
25
  #include <react/renderer/uimanager/UIManagerAnimationBackend.h>
25
26
  #include <chrono>
26
27
  #include <memory>
@@ -101,6 +102,8 @@ class NativeAnimatedNodesManager {
101
102
 
102
103
  void connectAnimatedNodeToView(Tag propsNodeTag, Tag viewTag) noexcept;
103
104
 
105
+ void connectAnimatedNodeToShadowNodeFamily(Tag propsNodeTag, std::shared_ptr<const ShadowNodeFamily> family) noexcept;
106
+
104
107
  void disconnectAnimatedNodes(Tag parentTag, Tag childTag) noexcept;
105
108
 
106
109
  void disconnectAnimatedNodeFromView(Tag propsNodeTag, Tag viewTag) noexcept;
@@ -258,6 +261,9 @@ class NativeAnimatedNodesManager {
258
261
  std::unordered_map<Tag, folly::dynamic> updateViewProps_{};
259
262
  std::unordered_map<Tag, folly::dynamic> updateViewPropsDirect_{};
260
263
 
264
+ mutable std::mutex tagToShadowNodeFamilyMutex_;
265
+ std::unordered_map<Tag, std::weak_ptr<const ShadowNodeFamily>> tagToShadowNodeFamily_{};
266
+
261
267
  /*
262
268
  * Sometimes a view is not longer connected to a PropsAnimatedNode, but
263
269
  * NativeAnimated has previously changed the view's props via direct
@@ -43,6 +43,7 @@ void FrameAnimationDriver::updateConfig(folly::dynamic config) {
43
43
  void FrameAnimationDriver::onConfigChanged() {
44
44
  auto frames = config_["frames"];
45
45
  react_native_assert(frames.type() == folly::dynamic::ARRAY);
46
+ frames_.clear();
46
47
  for (const auto& frame : frames) {
47
48
  auto frameValue = frame.asDouble();
48
49
  frames_.push_back(frameValue);
@@ -62,7 +62,6 @@ Color ColorAnimatedNode::getColor() {
62
62
  manager_->updatedNodeTags_.erase(tag_);
63
63
  }
64
64
  return color_;
65
- return 0;
66
65
  }
67
66
 
68
67
  } // namespace facebook::react
@@ -14,6 +14,7 @@
14
14
  #include "AnimatedNode.h"
15
15
 
16
16
  #include <react/renderer/animated/internal/primitives.h>
17
+ #include <react/renderer/core/ShadowNode.h>
17
18
  #include <mutex>
18
19
 
19
20
  namespace facebook::react {
@@ -22,7 +22,7 @@ RoundAnimatedNode::RoundAnimatedNode(
22
22
  NativeAnimatedNodesManager& manager)
23
23
  : ValueAnimatedNode(tag, config, manager),
24
24
  inputNodeTag_(static_cast<Tag>(getConfig()["input"].asInt())),
25
- nearest_(getConfig()["input"].asDouble()) {
25
+ nearest_(getConfig()["nearest"].asDouble()) {
26
26
  react_native_assert(
27
27
  nearest_ != 0 &&
28
28
  "'nearest' cannot be 0 (can't round to the nearest multiple of 0)");
@@ -55,7 +55,7 @@ bool ValueAnimatedNode::setOffset(double offset) noexcept {
55
55
  offset_ = offset;
56
56
  return true;
57
57
  }
58
- return true;
58
+ return false;
59
59
  }
60
60
 
61
61
  double ValueAnimatedNode::getValue() const noexcept {
@@ -215,6 +215,73 @@ TEST_F(AnimatedNodeTests, DiffClampAnimatedNode) {
215
215
  EXPECT_EQ(nodesManager_->getValue(diffClampTag), 1);
216
216
  }
217
217
 
218
+ TEST_F(AnimatedNodeTests, RoundAnimatedNodeUsesNearestConfigKey) {
219
+ // This test verifies that RoundAnimatedNode reads the "nearest" config key
220
+ // for the rounding factor, not the "input" key.
221
+ initNodesManager();
222
+
223
+ auto rootTag = getNextRootViewTag();
224
+
225
+ auto valueTag = ++rootTag;
226
+ auto roundTag = ++rootTag;
227
+
228
+ nodesManager_->createAnimatedNode(
229
+ valueTag,
230
+ folly::dynamic::object("type", "value")("value", 7.3)("offset", 0));
231
+
232
+ // The round node should read "nearest" for the rounding factor (5.0),
233
+ // not "input" (which is the valueTag integer).
234
+ nodesManager_->createAnimatedNode(
235
+ roundTag,
236
+ folly::dynamic::object("type", "round")("input", valueTag)(
237
+ "nearest", 5.0));
238
+ nodesManager_->connectAnimatedNodes(valueTag, roundTag);
239
+
240
+ runAnimationFrame(0);
241
+
242
+ // 7.3 rounded to nearest 5.0 should be 5.0 (since round(7.3/5) * 5 = 1 * 5)
243
+ // If the bug existed (reading "input" instead of "nearest"), it would use
244
+ // the valueTag as the rounding factor, giving incorrect results.
245
+ EXPECT_DOUBLE_EQ(nodesManager_->getValue(roundTag).value(), 5.0);
246
+
247
+ // Test another value to ensure rounding works correctly
248
+ nodesManager_->setAnimatedNodeValue(valueTag, 12.6);
249
+ runAnimationFrame(0);
250
+
251
+ // 12.6 rounded to nearest 5.0 should be 15.0 (since round(12.6/5) * 5 = 3 *
252
+ // 5)
253
+ EXPECT_DOUBLE_EQ(nodesManager_->getValue(roundTag).value(), 15.0);
254
+ }
255
+
256
+ TEST_F(AnimatedNodeTests, SetOffsetReturnsFalseWhenUnchanged) {
257
+ // This test verifies that setAnimatedNodeOffset doesn't trigger unnecessary
258
+ // updates when the offset value hasn't changed.
259
+ initNodesManager();
260
+
261
+ auto rootTag = getNextRootViewTag();
262
+ auto valueTag = ++rootTag;
263
+
264
+ nodesManager_->createAnimatedNode(
265
+ valueTag,
266
+ folly::dynamic::object("type", "value")("value", 10)("offset", 0));
267
+
268
+ runAnimationFrame(0);
269
+ EXPECT_EQ(nodeNeedsUpdate(valueTag), false);
270
+
271
+ // First setOffset should mark the node as needing update
272
+ nodesManager_->setAnimatedNodeOffset(valueTag, 5);
273
+ EXPECT_EQ(nodeNeedsUpdate(valueTag), true);
274
+ EXPECT_EQ(nodesManager_->getValue(valueTag), 15); // 10 + 5
275
+
276
+ runAnimationFrame(0);
277
+ EXPECT_EQ(nodeNeedsUpdate(valueTag), false);
278
+
279
+ // Setting the same offset again should NOT mark the node as needing update
280
+ nodesManager_->setAnimatedNodeOffset(valueTag, 5);
281
+ EXPECT_EQ(nodeNeedsUpdate(valueTag), false); // No change, no update needed
282
+ EXPECT_EQ(nodesManager_->getValue(valueTag), 15); // Still 10 + 5
283
+ }
284
+
218
285
  TEST_F(AnimatedNodeTests, ObjectAnimatedNode) {
219
286
  initNodesManager();
220
287
 
@@ -58,4 +58,63 @@ TEST_F(AnimationDriverTests, framesAnimation) {
58
58
  EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), toValue);
59
59
  }
60
60
 
61
+ TEST_F(AnimationDriverTests, framesAnimationReconfigurationClearsFrames) {
62
+ // This test verifies that when an animation is reconfigured via updateConfig,
63
+ // the frames array is cleared before adding new frames. Without clearing,
64
+ // frames would accumulate and cause incorrect animation behavior.
65
+ initNodesManager();
66
+
67
+ auto rootTag = getNextRootViewTag();
68
+
69
+ auto valueNodeTag = ++rootTag;
70
+ nodesManager_->createAnimatedNode(
71
+ valueNodeTag,
72
+ folly::dynamic::object("type", "value")("value", 0)("offset", 0));
73
+
74
+ const auto animationId = 1;
75
+ // First animation: 5 frames from 0 to 100
76
+ const auto frames1 = folly::dynamic::array(0.0f, 0.25f, 0.5f, 0.75f, 1.0f);
77
+ const auto toValue1 = 100;
78
+ nodesManager_->startAnimatingNode(
79
+ animationId,
80
+ valueNodeTag,
81
+ folly::dynamic::object("type", "frames")("frames", frames1)(
82
+ "toValue", toValue1),
83
+ std::nullopt);
84
+
85
+ const double startTimeInTick = 12345;
86
+
87
+ // Run first frame
88
+ runAnimationFrame(startTimeInTick);
89
+ EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), 0);
90
+
91
+ // Reconfigure the same animation (same animationId) with new frames
92
+ // This triggers updateConfig on the existing FrameAnimationDriver
93
+ const auto frames2 = folly::dynamic::array(0.0f, 0.5f, 1.0f);
94
+ const auto toValue2 = 200;
95
+ nodesManager_->startAnimatingNode(
96
+ animationId,
97
+ valueNodeTag,
98
+ folly::dynamic::object("type", "frames")("frames", frames2)(
99
+ "toValue", toValue2),
100
+ std::nullopt);
101
+
102
+ // Reset animation timing
103
+ const double newStartTimeInTick = 20000;
104
+
105
+ // Run animation at halfway point (1 frame into 3-frame animation)
106
+ runAnimationFrame(newStartTimeInTick);
107
+ runAnimationFrame(newStartTimeInTick + SingleFrameIntervalMs * 1);
108
+
109
+ // At frame 1 of 3 frames (50% progress), value should be approximately:
110
+ // startValue (0) + 0.5 * (toValue2 - startValue) = 0 + 0.5 * 200 = 100
111
+ // If frames accumulated (5 + 3 = 8 frames), we'd be at wrong position
112
+ // Use ceil rounding so 100.00x becomes 100.01
113
+ EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), 100.01);
114
+
115
+ // Complete the animation
116
+ runAnimationFrame(newStartTimeInTick + SingleFrameIntervalMs * 2);
117
+ EXPECT_EQ(round(nodesManager_->getValue(valueNodeTag).value()), toValue2);
118
+ }
119
+
61
120
  } // namespace facebook::react
@@ -0,0 +1,81 @@
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 "AnimatedPropsRegistry.h"
9
+ #include <react/renderer/core/PropsParserContext.h>
10
+ #include "AnimatedProps.h"
11
+
12
+ namespace facebook::react {
13
+
14
+ void AnimatedPropsRegistry::update(
15
+ const std::unordered_map<SurfaceId, SurfaceUpdates>& surfaceUpdates) {
16
+ auto lock = std::lock_guard(mutex_);
17
+ for (const auto& [surfaceId, updates] : surfaceUpdates) {
18
+ auto& surfaceContext = surfaceContexts_[surfaceId];
19
+ auto& pendingMap = surfaceContext.pendingMap;
20
+ auto& pendingFamilies = surfaceContext.pendingFamilies;
21
+
22
+ auto& updatesMap = updates.propsMap;
23
+ auto& updatesFamilies = updates.families;
24
+
25
+ for (auto& family : updatesFamilies) {
26
+ pendingFamilies.insert(family);
27
+ }
28
+
29
+ for (auto& [tag, animatedProps] : updatesMap) {
30
+ auto it = pendingMap.find(tag);
31
+ if (it == pendingMap.end()) {
32
+ it = pendingMap.insert_or_assign(tag, std::make_unique<PropsSnapshot>())
33
+ .first;
34
+ }
35
+ auto& snapshot = it->second;
36
+ auto& viewProps = snapshot->props;
37
+
38
+ for (const auto& animatedProp : animatedProps.props) {
39
+ snapshot->propNames.insert(animatedProp->propName);
40
+ cloneProp(viewProps, *animatedProp);
41
+ }
42
+ }
43
+ }
44
+ }
45
+
46
+ std::pair<std::unordered_set<const ShadowNodeFamily*>&, SnapshotMap&>
47
+ AnimatedPropsRegistry::getMap(SurfaceId surfaceId) {
48
+ auto lock = std::lock_guard(mutex_);
49
+ auto& [pendingMap, map, pendingFamilies, families] =
50
+ surfaceContexts_[surfaceId];
51
+
52
+ for (auto& family : pendingFamilies) {
53
+ families.insert(family);
54
+ }
55
+ for (auto& [tag, propsSnapshot] : pendingMap) {
56
+ auto currentIt = map.find(tag);
57
+ if (currentIt == map.end()) {
58
+ map.insert_or_assign(tag, std::move(propsSnapshot));
59
+ } else {
60
+ auto& currentSnapshot = currentIt->second;
61
+ for (auto& propName : propsSnapshot->propNames) {
62
+ currentSnapshot->propNames.insert(propName);
63
+ updateProp(propName, currentSnapshot->props, *propsSnapshot);
64
+ }
65
+ }
66
+ }
67
+ pendingMap.clear();
68
+ pendingFamilies.clear();
69
+
70
+ return {families, map};
71
+ }
72
+
73
+ void AnimatedPropsRegistry::clear(SurfaceId surfaceId) {
74
+ auto lock = std::lock_guard(mutex_);
75
+
76
+ auto& surfaceContext = surfaceContexts_[surfaceId];
77
+ surfaceContext.families.clear();
78
+ surfaceContext.map.clear();
79
+ }
80
+
81
+ } // namespace facebook::react
@@ -0,0 +1,83 @@
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 <folly/dynamic.h>
11
+ #include <react/renderer/components/view/BaseViewProps.h>
12
+ #include <react/renderer/core/ReactPrimitives.h>
13
+ #include <react/renderer/uimanager/UIManager.h>
14
+ #include <react/renderer/uimanager/UIManagerCommitHook.h>
15
+ #include "AnimatedProps.h"
16
+
17
+ namespace facebook::react {
18
+
19
+ struct PropsSnapshot {
20
+ BaseViewProps props;
21
+ std::unordered_set<PropName> propNames;
22
+ };
23
+
24
+ struct SurfaceContext {
25
+ std::unordered_map<Tag, std::unique_ptr<PropsSnapshot>> pendingMap, map;
26
+ std::unordered_set<const ShadowNodeFamily *> pendingFamilies, families;
27
+ };
28
+
29
+ struct SurfaceUpdates {
30
+ std::unordered_set<const ShadowNodeFamily *> families;
31
+ std::unordered_map<Tag, AnimatedProps> propsMap;
32
+ };
33
+
34
+ using SnapshotMap = std::unordered_map<Tag, std::unique_ptr<PropsSnapshot>>;
35
+
36
+ class AnimatedPropsRegistry {
37
+ public:
38
+ void update(const std::unordered_map<SurfaceId, SurfaceUpdates> &surfaceUpdates);
39
+ void clear(SurfaceId surfaceId);
40
+ std::pair<std::unordered_set<const ShadowNodeFamily *> &, SnapshotMap &> getMap(SurfaceId surfaceId);
41
+
42
+ private:
43
+ std::unordered_map<SurfaceId, SurfaceContext> surfaceContexts_;
44
+ std::mutex mutex_;
45
+ };
46
+
47
+ inline void updateProp(const PropName propName, BaseViewProps &viewProps, const PropsSnapshot &snapshot)
48
+ {
49
+ switch (propName) {
50
+ case OPACITY:
51
+ viewProps.opacity = snapshot.props.opacity;
52
+ break;
53
+
54
+ case WIDTH:
55
+ viewProps.yogaStyle.setDimension(
56
+ yoga::Dimension::Width, snapshot.props.yogaStyle.dimension(yoga::Dimension::Width));
57
+ break;
58
+
59
+ case HEIGHT: {
60
+ auto d = snapshot.props.yogaStyle.dimension(yoga::Dimension::Height);
61
+ viewProps.yogaStyle.setDimension(yoga::Dimension::Height, d);
62
+ break;
63
+ }
64
+
65
+ case TRANSFORM:
66
+ viewProps.transform = snapshot.props.transform;
67
+ break;
68
+
69
+ case BORDER_RADII:
70
+ viewProps.borderRadii = snapshot.props.borderRadii;
71
+ break;
72
+
73
+ case FLEX:
74
+ viewProps.yogaStyle.setFlex(snapshot.props.yogaStyle.flex());
75
+ break;
76
+
77
+ case BACKGROUND_COLOR:
78
+ viewProps.backgroundColor = snapshot.props.backgroundColor;
79
+ break;
80
+ }
81
+ }
82
+
83
+ } // namespace facebook::react
@@ -9,6 +9,7 @@
9
9
  #include <react/renderer/animationbackend/AnimatedPropsSerializer.h>
10
10
  #include <react/renderer/graphics/Color.h>
11
11
  #include <chrono>
12
+ #include "AnimatedPropsRegistry.h"
12
13
 
13
14
  namespace facebook::react {
14
15
 
@@ -70,12 +71,14 @@ AnimationBackend::AnimationBackend(
70
71
  stopOnRenderCallback_(std::move(stopOnRenderCallback)),
71
72
  directManipulationCallback_(std::move(directManipulationCallback)),
72
73
  fabricCommitCallback_(std::move(fabricCommitCallback)),
73
- uiManager_(uiManager) {}
74
+ animatedPropsRegistry_(std::make_shared<AnimatedPropsRegistry>()),
75
+ uiManager_(uiManager),
76
+ commitHook_(uiManager, animatedPropsRegistry_) {}
74
77
 
75
78
  void AnimationBackend::onAnimationFrame(double timestamp) {
76
- std::unordered_map<Tag, AnimatedProps> updates;
77
- std::unordered_map<SurfaceId, std::unordered_set<const ShadowNodeFamily*>>
78
- surfaceToFamilies;
79
+ std::unordered_map<Tag, AnimatedProps> synchronousUpdates;
80
+ std::unordered_map<SurfaceId, SurfaceUpdates> surfaceUpdates;
81
+
79
82
  bool hasAnyLayoutUpdates = false;
80
83
  for (auto& callback : callbacks) {
81
84
  auto muatations = callback(static_cast<float>(timestamp));
@@ -83,22 +86,28 @@ void AnimationBackend::onAnimationFrame(double timestamp) {
83
86
  hasAnyLayoutUpdates |= mutationHasLayoutUpdates(mutation);
84
87
  const auto family = mutation.family;
85
88
  if (family != nullptr) {
86
- surfaceToFamilies[family->getSurfaceId()].insert(family);
89
+ auto& [families, updates] = surfaceUpdates[family->getSurfaceId()];
90
+ families.insert(family.get());
91
+ updates[mutation.tag] = std::move(mutation.props);
92
+ } else {
93
+ synchronousUpdates[mutation.tag] = std::move(mutation.props);
87
94
  }
88
- updates[mutation.tag] = std::move(mutation.props);
89
95
  }
90
96
  }
91
97
 
98
+ animatedPropsRegistry_->update(surfaceUpdates);
99
+
92
100
  if (hasAnyLayoutUpdates) {
93
- commitUpdates(surfaceToFamilies, updates);
101
+ commitUpdates(surfaceUpdates);
94
102
  } else {
95
- synchronouslyUpdateProps(updates);
103
+ synchronouslyUpdateProps(synchronousUpdates);
96
104
  }
97
105
  }
98
106
 
99
107
  void AnimationBackend::start(const Callback& callback, bool isAsync) {
100
108
  callbacks.push_back(callback);
101
- // TODO: startOnRenderCallback_ should provide the timestamp from the platform
109
+ // TODO: startOnRenderCallback_ should provide the timestamp from the
110
+ // platform
102
111
  if (startOnRenderCallback_) {
103
112
  startOnRenderCallback_(
104
113
  [this]() {
@@ -117,13 +126,11 @@ void AnimationBackend::stop(bool isAsync) {
117
126
  }
118
127
 
119
128
  void AnimationBackend::commitUpdates(
120
- const std::unordered_map<
121
- SurfaceId,
122
- std::unordered_set<const ShadowNodeFamily*>>& surfaceToFamilies,
123
- std::unordered_map<Tag, AnimatedProps>& updates) {
124
- for (const auto& surfaceEntry : surfaceToFamilies) {
129
+ std::unordered_map<SurfaceId, SurfaceUpdates>& surfaceUpdates) {
130
+ for (auto& surfaceEntry : surfaceUpdates) {
125
131
  const auto& surfaceId = surfaceEntry.first;
126
- const auto& surfaceFamilies = surfaceEntry.second;
132
+ const auto& surfaceFamilies = surfaceEntry.second.families;
133
+ auto& updates = surfaceEntry.second.propsMap;
127
134
  uiManager_->getShadowTreeRegistry().visit(
128
135
  surfaceId, [&surfaceFamilies, &updates](const ShadowTree& shadowTree) {
129
136
  shadowTree.commit(
@@ -165,4 +172,8 @@ void AnimationBackend::synchronouslyUpdateProps(
165
172
  }
166
173
  }
167
174
 
175
+ void AnimationBackend::clearRegistry(SurfaceId surfaceId) {
176
+ animatedPropsRegistry_->clear(surfaceId);
177
+ }
178
+
168
179
  } // namespace facebook::react
@@ -15,6 +15,8 @@
15
15
  #include <vector>
16
16
  #include "AnimatedProps.h"
17
17
  #include "AnimatedPropsBuilder.h"
18
+ #include "AnimatedPropsRegistry.h"
19
+ #include "AnimationBackendCommitHook.h"
18
20
 
19
21
  namespace facebook::react {
20
22
 
@@ -32,7 +34,7 @@ class UIManagerNativeAnimatedDelegateBackendImpl : public UIManagerNativeAnimate
32
34
 
33
35
  struct AnimationMutation {
34
36
  Tag tag;
35
- const ShadowNodeFamily *family;
37
+ std::shared_ptr<const ShadowNodeFamily> family;
36
38
  AnimatedProps props;
37
39
  };
38
40
 
@@ -51,7 +53,9 @@ class AnimationBackend : public UIManagerAnimationBackend {
51
53
  const StopOnRenderCallback stopOnRenderCallback_;
52
54
  const DirectManipulationCallback directManipulationCallback_;
53
55
  const FabricCommitCallback fabricCommitCallback_;
56
+ std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
54
57
  UIManager *uiManager_;
58
+ AnimationBackendCommitHook commitHook_;
55
59
 
56
60
  AnimationBackend(
57
61
  StartOnRenderCallback &&startOnRenderCallback,
@@ -59,10 +63,9 @@ class AnimationBackend : public UIManagerAnimationBackend {
59
63
  DirectManipulationCallback &&directManipulationCallback,
60
64
  FabricCommitCallback &&fabricCommitCallback,
61
65
  UIManager *uiManager);
62
- void commitUpdates(
63
- const std::unordered_map<SurfaceId, std::unordered_set<const ShadowNodeFamily *>> &surfaceToFamilies,
64
- std::unordered_map<Tag, AnimatedProps> &updates);
66
+ void commitUpdates(std::unordered_map<SurfaceId, SurfaceUpdates> &surfaceUpdates);
65
67
  void synchronouslyUpdateProps(const std::unordered_map<Tag, AnimatedProps> &updates);
68
+ void clearRegistry(SurfaceId surfaceId) override;
66
69
 
67
70
  void onAnimationFrame(double timestamp) override;
68
71
  void start(const Callback &callback, bool isAsync);
@@ -0,0 +1,69 @@
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 <react/renderer/animationbackend/AnimationBackendCommitHook.h>
9
+
10
+ namespace facebook::react {
11
+
12
+ AnimationBackendCommitHook::AnimationBackendCommitHook(
13
+ UIManager* uiManager,
14
+ std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry)
15
+ : animatedPropsRegistry_(std::move(animatedPropsRegistry)) {
16
+ uiManager->registerCommitHook(*this);
17
+ }
18
+
19
+ RootShadowNode::Unshared AnimationBackendCommitHook::shadowTreeWillCommit(
20
+ const ShadowTree& shadowTree,
21
+ const RootShadowNode::Shared& oldRootShadowNode,
22
+ const RootShadowNode::Unshared& newRootShadowNode,
23
+ const ShadowTreeCommitOptions& commitOptions) noexcept {
24
+ if (commitOptions.source != ShadowTreeCommitSource::React) {
25
+ return newRootShadowNode;
26
+ }
27
+
28
+ const auto& res = animatedPropsRegistry_->getMap(shadowTree.getSurfaceId());
29
+ auto& surfaceFamilies = res.first;
30
+ auto& updates = res.second;
31
+
32
+ if (surfaceFamilies.empty()) {
33
+ return newRootShadowNode;
34
+ }
35
+ return std::static_pointer_cast<RootShadowNode>(
36
+ newRootShadowNode->cloneMultiple(
37
+ surfaceFamilies,
38
+ [&surfaceFamilies, &updates](
39
+ const ShadowNode& shadowNode,
40
+ const ShadowNodeFragment& fragment) {
41
+ auto newProps = ShadowNodeFragment::propsPlaceholder();
42
+ std::shared_ptr<BaseViewProps> viewProps = nullptr;
43
+ if (surfaceFamilies.contains(&shadowNode.getFamily()) &&
44
+ updates.contains(shadowNode.getTag())) {
45
+ auto& snapshot = updates.at(shadowNode.getTag());
46
+ if (!snapshot->propNames.empty()) {
47
+ PropsParserContext propsParserContext{
48
+ shadowNode.getSurfaceId(),
49
+ *shadowNode.getContextContainer()};
50
+
51
+ newProps = shadowNode.getComponentDescriptor().cloneProps(
52
+ propsParserContext, shadowNode.getProps(), {});
53
+ viewProps = std::const_pointer_cast<BaseViewProps>(
54
+ std::static_pointer_cast<const BaseViewProps>(newProps));
55
+ }
56
+
57
+ for (const auto& propName : snapshot->propNames) {
58
+ updateProp(propName, *viewProps, *snapshot);
59
+ }
60
+ }
61
+ return shadowNode.clone(
62
+ {.props = newProps,
63
+ .children = fragment.children,
64
+ .state = shadowNode.getState(),
65
+ .runtimeShadowNodeReference = true});
66
+ }));
67
+ }
68
+
69
+ } // namespace facebook::react
@@ -0,0 +1,32 @@
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 <folly/dynamic.h>
11
+ #include <react/renderer/core/ReactPrimitives.h>
12
+ #include <react/renderer/uimanager/UIManager.h>
13
+ #include <react/renderer/uimanager/UIManagerCommitHook.h>
14
+ #include "AnimatedPropsRegistry.h"
15
+
16
+ namespace facebook::react {
17
+
18
+ class AnimationBackendCommitHook : public UIManagerCommitHook {
19
+ std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
20
+
21
+ public:
22
+ AnimationBackendCommitHook(UIManager *uiManager, std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry);
23
+ RootShadowNode::Unshared shadowTreeWillCommit(
24
+ const ShadowTree &shadowTree,
25
+ const RootShadowNode::Shared &oldRootShadowNode,
26
+ const RootShadowNode::Unshared &newRootShadowNode,
27
+ const ShadowTreeCommitOptions &commitOptions) noexcept override;
28
+ void commitHookWasRegistered(const UIManager &uiManager) noexcept override {}
29
+ void commitHookWasUnregistered(const UIManager &uiManager) noexcept override {}
30
+ };
31
+
32
+ } // namespace facebook::react
@@ -205,6 +205,12 @@ void UIManager::completeSurface(
205
205
  // after we commit a specific one.
206
206
  lazyShadowTreeRevisionConsistencyManager_->updateCurrentRevision(
207
207
  surfaceId, shadowTree.getCurrentRevision().rootShadowNode);
208
+
209
+ if (ReactNativeFeatureFlags::useSharedAnimatedBackend()) {
210
+ if (auto animationBackend = animationBackend_.lock()) {
211
+ animationBackend->clearRegistry(surfaceId);
212
+ }
213
+ }
208
214
  }
209
215
  });
210
216
  }
@@ -19,6 +19,7 @@ class UIManagerAnimationBackend {
19
19
  virtual void onAnimationFrame(double timestamp) = 0;
20
20
  // TODO: T240293839 Move over start() function and mutation types
21
21
  virtual void stop(bool isAsync) = 0;
22
+ virtual void clearRegistry(SurfaceId surfaceId) = 0;
22
23
  };
23
24
 
24
25
  } // namespace facebook::react
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native",
3
- "version": "0.84.0-nightly-20251205-95cc1e767",
3
+ "version": "0.84.0-nightly-20251206-63b0aef13",
4
4
  "description": "A framework for building native apps using React",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -160,13 +160,13 @@
160
160
  },
161
161
  "dependencies": {
162
162
  "@jest/create-cache-key-function": "^29.7.0",
163
- "@react-native/assets-registry": "0.84.0-nightly-20251205-95cc1e767",
164
- "@react-native/codegen": "0.84.0-nightly-20251205-95cc1e767",
165
- "@react-native/community-cli-plugin": "0.84.0-nightly-20251205-95cc1e767",
166
- "@react-native/gradle-plugin": "0.84.0-nightly-20251205-95cc1e767",
167
- "@react-native/js-polyfills": "0.84.0-nightly-20251205-95cc1e767",
168
- "@react-native/normalize-colors": "0.84.0-nightly-20251205-95cc1e767",
169
- "@react-native/virtualized-lists": "0.84.0-nightly-20251205-95cc1e767",
163
+ "@react-native/assets-registry": "0.84.0-nightly-20251206-63b0aef13",
164
+ "@react-native/codegen": "0.84.0-nightly-20251206-63b0aef13",
165
+ "@react-native/community-cli-plugin": "0.84.0-nightly-20251206-63b0aef13",
166
+ "@react-native/gradle-plugin": "0.84.0-nightly-20251206-63b0aef13",
167
+ "@react-native/js-polyfills": "0.84.0-nightly-20251206-63b0aef13",
168
+ "@react-native/normalize-colors": "0.84.0-nightly-20251206-63b0aef13",
169
+ "@react-native/virtualized-lists": "0.84.0-nightly-20251206-63b0aef13",
170
170
  "abort-controller": "^3.0.0",
171
171
  "anser": "^1.4.9",
172
172
  "ansi-regex": "^5.0.0",
@@ -175,7 +175,7 @@
175
175
  "base64-js": "^1.5.1",
176
176
  "commander": "^12.0.0",
177
177
  "flow-enums-runtime": "^0.0.6",
178
- "hermes-compiler": "0.14.0-commitly-202512030604-3713fb12a",
178
+ "hermes-compiler": "0.14.0-commitly-202512051838-1e8c447f2",
179
179
  "invariant": "^2.2.4",
180
180
  "jest-environment-node": "^29.7.0",
181
181
  "memoize-one": "^5.0.0",
@@ -1,2 +1,2 @@
1
- HERMES_VERSION_NAME=0.14.0-commitly-202512030604-3713fb12a
1
+ HERMES_VERSION_NAME=0.14.0-commitly-202512051838-1e8c447f2
2
2
  HERMES_V1_VERSION_NAME=250829098.0.4