react-native-reanimated 3.15.1 → 3.15.3

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.
@@ -25,22 +25,24 @@ class PropsRegistry {
25
25
 
26
26
  void remove(const Tag tag);
27
27
 
28
- void pleaseSkipReanimatedCommit() {
29
- shouldReanimatedSkipCommit_ = true;
28
+ void pauseReanimatedCommits() {
29
+ isPaused_ = true;
30
30
  }
31
31
 
32
32
  bool shouldReanimatedSkipCommit() {
33
- #if REACT_NATIVE_MINOR_VERSION >= 73
34
- // In RN 0.73+ we have a mount hook that will properly unset this flag
35
- // after a non-Reanimated commit.
36
- return shouldReanimatedSkipCommit_;
37
- #else
38
- return shouldReanimatedSkipCommit_.exchange(false);
39
- #endif
33
+ return isPaused_;
40
34
  }
41
35
 
42
- void resetReanimatedSkipCommitFlag() {
43
- shouldReanimatedSkipCommit_ = false;
36
+ void unpauseReanimatedCommits() {
37
+ isPaused_ = false;
38
+ }
39
+
40
+ void pleaseCommitAfterPause() {
41
+ shouldCommitAfterPause_ = true;
42
+ }
43
+
44
+ bool shouldCommitAfterPause() {
45
+ return shouldCommitAfterPause_.exchange(false);
44
46
  }
45
47
 
46
48
  private:
@@ -48,7 +50,8 @@ class PropsRegistry {
48
50
 
49
51
  mutable std::mutex mutex_; // Protects `map_`.
50
52
 
51
- std::atomic<bool> shouldReanimatedSkipCommit_;
53
+ std::atomic<bool> isPaused_;
54
+ std::atomic<bool> shouldCommitAfterPause_;
52
55
  };
53
56
 
54
57
  } // namespace reanimated
@@ -26,12 +26,7 @@ ReanimatedCommitHook::~ReanimatedCommitHook() noexcept {
26
26
  RootShadowNode::Unshared ReanimatedCommitHook::shadowTreeWillCommit(
27
27
  ShadowTree const &,
28
28
  RootShadowNode::Shared const &,
29
- #if REACT_NATIVE_MINOR_VERSION >= 73
30
29
  RootShadowNode::Unshared const &newRootShadowNode) noexcept {
31
- #else
32
- RootShadowNode::Unshared const &newRootShadowNode) const noexcept {
33
- #endif
34
-
35
30
  auto reaShadowNode =
36
31
  std::reinterpret_pointer_cast<ReanimatedCommitShadowNode>(
37
32
  newRootShadowNode);
@@ -40,11 +35,12 @@ RootShadowNode::Unshared ReanimatedCommitHook::shadowTreeWillCommit(
40
35
  // ShadowTree commited by Reanimated, no need to apply updates from
41
36
  // PropsRegistry
42
37
  reaShadowNode->unsetReanimatedCommitTrait();
38
+ reaShadowNode->setReanimatedMountTrait();
43
39
  return newRootShadowNode;
44
40
  }
45
41
 
46
42
  // ShadowTree not commited by Reanimated, apply updates from PropsRegistry
47
-
43
+ reaShadowNode->unsetReanimatedMountTrait();
48
44
  RootShadowNode::Unshared rootNode = newRootShadowNode;
49
45
  PropsMap propsMap;
50
46
 
@@ -58,12 +54,15 @@ RootShadowNode::Unshared ReanimatedCommitHook::shadowTreeWillCommit(
58
54
 
59
55
  rootNode = cloneShadowTreeWithNewProps(*rootNode, propsMap);
60
56
 
61
- // If the commit comes from React Native then skip one commit from
57
+ // If the commit comes from React Native then pause commits from
62
58
  // Reanimated since the ShadowTree to be committed by Reanimated may not
63
59
  // include the new changes from React Native yet and all changes of animated
64
60
  // props will be applied in ReanimatedCommitHook by iterating over
65
61
  // PropsRegistry.
66
- propsRegistry_->pleaseSkipReanimatedCommit();
62
+ // This is very important, since if we didn't pause Reanimated commits,
63
+ // it could lead to RN commits being delayed until the animation is finished
64
+ // (very bad).
65
+ propsRegistry_->pauseReanimatedCommits();
67
66
  }
68
67
 
69
68
  return rootNode;
@@ -9,8 +9,14 @@ namespace reanimated {
9
9
  // We need this information to skip unnecessary updates in
10
10
  // the commit hook.
11
11
  // Currently RN traits go up to 10, so hopefully
12
- // the arbitrarily chosen number 27 will be safe :)
12
+ // the arbitrarily chosen numbers 27 and 28 will be safe :)
13
+
14
+ // We have to use 2 traits, because we want to distinguish reanimated
15
+ // commits both in the commit hook and mount hook. If we only had one trait
16
+ // and didn't remove it in the commit hook, then any node that would clone
17
+ // this node would also have our commit trait, rendering this trait useless.
13
18
  constexpr ShadowNodeTraits::Trait ReanimatedCommitTrait{1 << 27};
19
+ constexpr ShadowNodeTraits::Trait ReanimatedMountTrait{1 << 28};
14
20
 
15
21
  class ReanimatedCommitShadowNode : public ShadowNode {
16
22
  public:
@@ -23,6 +29,15 @@ class ReanimatedCommitShadowNode : public ShadowNode {
23
29
  inline bool hasReanimatedCommitTrait() {
24
30
  return traits_.check(ReanimatedCommitTrait);
25
31
  }
32
+ inline void setReanimatedMountTrait() {
33
+ traits_.set(ReanimatedMountTrait);
34
+ }
35
+ inline void unsetReanimatedMountTrait() {
36
+ traits_.unset(ReanimatedMountTrait);
37
+ }
38
+ inline bool hasReanimatedMountTrait() {
39
+ return traits_.check(ReanimatedMountTrait);
40
+ }
26
41
  };
27
42
 
28
43
  } // namespace reanimated
@@ -0,0 +1,82 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+
3
+ #include "ReanimatedMountHook.h"
4
+ #include "ReanimatedCommitShadowNode.h"
5
+
6
+ namespace reanimated {
7
+
8
+ ReanimatedMountHook::ReanimatedMountHook(
9
+ const std::shared_ptr<PropsRegistry> &propsRegistry,
10
+ const std::shared_ptr<UIManager> &uiManager)
11
+ : propsRegistry_(propsRegistry), uiManager_(uiManager) {
12
+ uiManager_->registerMountHook(*this);
13
+ }
14
+
15
+ ReanimatedMountHook::~ReanimatedMountHook() noexcept {
16
+ uiManager_->unregisterMountHook(*this);
17
+ }
18
+
19
+ void ReanimatedMountHook::shadowTreeDidMount(
20
+ RootShadowNode::Shared const &rootShadowNode,
21
+ double) noexcept {
22
+ auto reaShadowNode =
23
+ std::reinterpret_pointer_cast<ReanimatedCommitShadowNode>(
24
+ std::const_pointer_cast<RootShadowNode>(rootShadowNode));
25
+
26
+ if (reaShadowNode->hasReanimatedMountTrait()) {
27
+ // We mark reanimated commits with ReanimatedMountTrait. We don't want other
28
+ // shadow nodes to use this trait, but since this rootShadowNode is Shared,
29
+ // we don't have that guarantee. That's why we also unset this trait in the
30
+ // commit hook. We remove it here mainly for the sake of cleanliness.
31
+ reaShadowNode->unsetReanimatedMountTrait();
32
+ return;
33
+ }
34
+
35
+ // When commit from React Native has finished, we reset the skip commit flag
36
+ // in order to allow Reanimated to commit its tree
37
+ propsRegistry_->unpauseReanimatedCommits();
38
+ if (!propsRegistry_->shouldCommitAfterPause()) {
39
+ return;
40
+ }
41
+
42
+ const auto &shadowTreeRegistry = uiManager_->getShadowTreeRegistry();
43
+ shadowTreeRegistry.visit(
44
+ rootShadowNode->getSurfaceId(), [&](ShadowTree const &shadowTree) {
45
+ shadowTree.commit(
46
+ [&](RootShadowNode const &oldRootShadowNode)
47
+ -> RootShadowNode::Unshared {
48
+ PropsMap propsMap;
49
+
50
+ RootShadowNode::Unshared rootNode =
51
+ std::static_pointer_cast<RootShadowNode>(
52
+ oldRootShadowNode.ShadowNode::clone({}));
53
+ {
54
+ auto lock = propsRegistry_->createLock();
55
+
56
+ propsRegistry_->for_each([&](const ShadowNodeFamily &family,
57
+ const folly::dynamic &props) {
58
+ propsMap[&family].emplace_back(props);
59
+ });
60
+
61
+ rootNode =
62
+ cloneShadowTreeWithNewProps(oldRootShadowNode, propsMap);
63
+ }
64
+
65
+ // Mark the commit as Reanimated commit so that we can
66
+ // distinguish it in ReanimatedCommitHook.
67
+ auto reaShadowNode =
68
+ std::reinterpret_pointer_cast<ReanimatedCommitShadowNode>(
69
+ rootNode);
70
+ reaShadowNode->setReanimatedCommitTrait();
71
+
72
+ return rootNode;
73
+ },
74
+ {/* .enableStateReconciliation = */
75
+ false,
76
+ /* .mountSynchronously = */ true});
77
+ });
78
+ }
79
+
80
+ } // namespace reanimated
81
+
82
+ #endif // RCT_NEW_ARCH_ENABLED
@@ -0,0 +1,33 @@
1
+ #pragma once
2
+ #ifdef RCT_NEW_ARCH_ENABLED
3
+
4
+ #include "PropsRegistry.h"
5
+
6
+ #include <react/renderer/uimanager/UIManagerMountHook.h>
7
+ #include "ShadowTreeCloner.h"
8
+
9
+ #include <memory>
10
+
11
+ namespace reanimated {
12
+
13
+ using namespace facebook::react;
14
+
15
+ class ReanimatedMountHook : public UIManagerMountHook {
16
+ public:
17
+ ReanimatedMountHook(
18
+ const std::shared_ptr<PropsRegistry> &propsRegistry,
19
+ const std::shared_ptr<UIManager> &uiManager);
20
+ ~ReanimatedMountHook() noexcept override;
21
+
22
+ void shadowTreeDidMount(
23
+ RootShadowNode::Shared const &rootShadowNode,
24
+ double mountTime) noexcept override;
25
+
26
+ private:
27
+ const std::shared_ptr<PropsRegistry> propsRegistry_;
28
+ const std::shared_ptr<UIManager> uiManager_;
29
+ };
30
+
31
+ } // namespace reanimated
32
+
33
+ #endif // RCT_NEW_ARCH_ENABLED
@@ -22,7 +22,8 @@ std::optional<MountingTransaction> LayoutAnimationsProxy::pullTransaction(
22
22
  const TransactionTelemetry &telemetry,
23
23
  ShadowViewMutationList mutations) const {
24
24
  #ifdef LAYOUT_ANIMATIONS_LOGS
25
- LOG(INFO) << "\npullTransaction " << std::this_thread::get_id() << " "
25
+ LOG(INFO) << std::endl;
26
+ LOG(INFO) << "pullTransaction " << std::this_thread::get_id() << " "
26
27
  << surfaceId << std::endl;
27
28
  #endif
28
29
  auto lock = std::unique_lock<std::recursive_mutex>(mutex);
@@ -240,15 +241,15 @@ void LayoutAnimationsProxy::handleRemovals(
240
241
  if (!startAnimationsRecursively(
241
242
  node, true, true, false, filteredMutations)) {
242
243
  filteredMutations.push_back(node->mutation);
243
- nodeForTag_.erase(node->tag);
244
244
  node->unflattenedParent->removeChildFromUnflattenedTree(node); //???
245
- #ifdef LAYOUT_ANIMATIONS_LOGS
246
- LOG(INFO) << "delete " << node->tag << std::endl;
247
- #endif
248
245
  if (node->state != MOVED) {
249
246
  maybeCancelAnimation(node->tag);
250
247
  filteredMutations.push_back(ShadowViewMutation::DeleteMutation(
251
248
  node->mutation.oldChildShadowView));
249
+ nodeForTag_.erase(node->tag);
250
+ #ifdef LAYOUT_ANIMATIONS_LOGS
251
+ LOG(INFO) << "delete " << node->tag << std::endl;
252
+ #endif
252
253
  }
253
254
  }
254
255
  }
@@ -268,6 +269,7 @@ void LayoutAnimationsProxy::handleUpdatesAndEnterings(
268
269
  ShadowViewMutationList &mutations,
269
270
  const PropsParserContext &propsParserContext,
270
271
  SurfaceId surfaceId) const {
272
+ std::unordered_map<Tag, ShadowView> oldShadowViewsForReparentings;
271
273
  for (auto &mutation : mutations) {
272
274
  maybeUpdateWindowDimensions(mutation, surfaceId);
273
275
 
@@ -291,7 +293,14 @@ void LayoutAnimationsProxy::handleUpdatesAndEnterings(
291
293
  if (movedViews.contains(tag)) {
292
294
  auto layoutAnimationIt = layoutAnimations_.find(tag);
293
295
  if (layoutAnimationIt == layoutAnimations_.end()) {
294
- filteredMutations.push_back(mutation);
296
+ if (oldShadowViewsForReparentings.contains(tag)) {
297
+ filteredMutations.push_back(ShadowViewMutation::InsertMutation(
298
+ mutation.parentShadowView,
299
+ oldShadowViewsForReparentings[tag],
300
+ mutation.index));
301
+ } else {
302
+ filteredMutations.push_back(mutation);
303
+ }
295
304
  continue;
296
305
  }
297
306
 
@@ -338,6 +347,10 @@ void LayoutAnimationsProxy::handleUpdatesAndEnterings(
338
347
  updateOngoingAnimationTarget(tag, mutation);
339
348
  continue;
340
349
  }
350
+
351
+ // store the oldChildShadowView, so that we can use this ShadowView when
352
+ // the view is inserted
353
+ oldShadowViewsForReparentings[tag] = mutation.oldChildShadowView;
341
354
  startLayoutAnimation(tag, mutation);
342
355
  break;
343
356
  }
@@ -471,6 +484,7 @@ bool LayoutAnimationsProxy::startAnimationsRecursively(
471
484
  hasAnimatedChildren = true;
472
485
  } else {
473
486
  endAnimationsRecursively(subNode, mutations);
487
+ toBeRemoved.push_back(subNode);
474
488
  }
475
489
  } else if (startAnimationsRecursively(
476
490
  subNode,
@@ -486,7 +500,7 @@ bool LayoutAnimationsProxy::startAnimationsRecursively(
486
500
  hasAnimatedChildren = true;
487
501
  } else if (subNode->state == MOVED) {
488
502
  mutations.push_back(subNode->mutation);
489
- nodeForTag_.erase(subNode->tag);
503
+ toBeRemoved.push_back(subNode);
490
504
  } else if (shouldRemoveSubviewsWithoutAnimations) {
491
505
  maybeCancelAnimation(subNode->tag);
492
506
  mutations.push_back(subNode->mutation);
@@ -508,6 +522,14 @@ bool LayoutAnimationsProxy::startAnimationsRecursively(
508
522
  }
509
523
 
510
524
  if (node->state == MOVED) {
525
+ auto replacement = std::make_shared<Node>(*node);
526
+ for (auto subNode : node->children) {
527
+ subNode->parent = replacement;
528
+ }
529
+ for (auto subNode : node->unflattenedChildren) {
530
+ subNode->unflattenedParent = replacement;
531
+ }
532
+ nodeForTag_[replacement->tag] = replacement;
511
533
  return false;
512
534
  }
513
535
 
@@ -594,30 +616,37 @@ void LayoutAnimationsProxy::startEnteringAnimation(
594
616
  LOG(INFO) << "start entering animation for tag " << tag << std::endl;
595
617
  #endif
596
618
  auto finalView = std::make_shared<ShadowView>(mutation.newChildShadowView);
597
- auto current = std::make_shared<ShadowView>(mutation.oldChildShadowView);
619
+ auto current = std::make_shared<ShadowView>(mutation.newChildShadowView);
598
620
  auto parent = std::make_shared<ShadowView>(mutation.parentShadowView);
599
621
 
600
622
  auto &viewProps =
601
623
  static_cast<const ViewProps &>(*mutation.newChildShadowView.props);
602
- layoutAnimations_.insert_or_assign(
603
- tag, LayoutAnimation{finalView, current, parent, viewProps.opacity});
624
+ auto opacity = viewProps.opacity;
625
+
626
+ uiScheduler_->scheduleOnUI(
627
+ [finalView, current, parent, mutation, opacity, this, tag]() {
628
+ Rect window{};
629
+ {
630
+ auto lock = std::unique_lock<std::recursive_mutex>(mutex);
631
+ layoutAnimations_.insert_or_assign(
632
+ tag, LayoutAnimation{finalView, current, parent, opacity});
633
+ window =
634
+ surfaceManager.getWindow(mutation.newChildShadowView.surfaceId);
635
+ }
604
636
 
605
- Snapshot values(
606
- mutation.newChildShadowView,
607
- surfaceManager.getWindow(mutation.newChildShadowView.surfaceId));
608
- uiScheduler_->scheduleOnUI([values, this, tag]() {
609
- jsi::Object yogaValues(uiRuntime_);
610
- yogaValues.setProperty(uiRuntime_, "targetOriginX", values.x);
611
- yogaValues.setProperty(uiRuntime_, "targetGlobalOriginX", values.x);
612
- yogaValues.setProperty(uiRuntime_, "targetOriginY", values.y);
613
- yogaValues.setProperty(uiRuntime_, "targetGlobalOriginY", values.y);
614
- yogaValues.setProperty(uiRuntime_, "targetWidth", values.width);
615
- yogaValues.setProperty(uiRuntime_, "targetHeight", values.height);
616
- yogaValues.setProperty(uiRuntime_, "windowWidth", values.windowWidth);
617
- yogaValues.setProperty(uiRuntime_, "windowHeight", values.windowHeight);
618
- layoutAnimationsManager_->startLayoutAnimation(
619
- uiRuntime_, tag, LayoutAnimationType::ENTERING, yogaValues);
620
- });
637
+ Snapshot values(mutation.newChildShadowView, window);
638
+ jsi::Object yogaValues(uiRuntime_);
639
+ yogaValues.setProperty(uiRuntime_, "targetOriginX", values.x);
640
+ yogaValues.setProperty(uiRuntime_, "targetGlobalOriginX", values.x);
641
+ yogaValues.setProperty(uiRuntime_, "targetOriginY", values.y);
642
+ yogaValues.setProperty(uiRuntime_, "targetGlobalOriginY", values.y);
643
+ yogaValues.setProperty(uiRuntime_, "targetWidth", values.width);
644
+ yogaValues.setProperty(uiRuntime_, "targetHeight", values.height);
645
+ yogaValues.setProperty(uiRuntime_, "windowWidth", values.windowWidth);
646
+ yogaValues.setProperty(uiRuntime_, "windowHeight", values.windowHeight);
647
+ layoutAnimationsManager_->startLayoutAnimation(
648
+ uiRuntime_, tag, LayoutAnimationType::ENTERING, yogaValues);
649
+ });
621
650
  }
622
651
 
623
652
  void LayoutAnimationsProxy::startExitingAnimation(
@@ -627,12 +656,18 @@ void LayoutAnimationsProxy::startExitingAnimation(
627
656
  LOG(INFO) << "start exiting animation for tag " << tag << std::endl;
628
657
  #endif
629
658
  auto surfaceId = mutation.oldChildShadowView.surfaceId;
630
- auto oldView = mutation.oldChildShadowView;
631
- createLayoutAnimation(mutation, oldView, surfaceId, tag);
632
659
 
633
- Snapshot values(oldView, surfaceManager.getWindow(surfaceId));
660
+ uiScheduler_->scheduleOnUI([this, tag, mutation, surfaceId]() {
661
+ auto oldView = mutation.oldChildShadowView;
662
+ Rect window{};
663
+ {
664
+ auto lock = std::unique_lock<std::recursive_mutex>(mutex);
665
+ createLayoutAnimation(mutation, oldView, surfaceId, tag);
666
+ window = surfaceManager.getWindow(surfaceId);
667
+ }
668
+
669
+ Snapshot values(oldView, window);
634
670
 
635
- uiScheduler_->scheduleOnUI([values, this, tag]() {
636
671
  jsi::Object yogaValues(uiRuntime_);
637
672
  yogaValues.setProperty(uiRuntime_, "currentOriginX", values.x);
638
673
  yogaValues.setProperty(uiRuntime_, "currentGlobalOriginX", values.x);
@@ -655,14 +690,19 @@ void LayoutAnimationsProxy::startLayoutAnimation(
655
690
  LOG(INFO) << "start layout animation for tag " << tag << std::endl;
656
691
  #endif
657
692
  auto surfaceId = mutation.oldChildShadowView.surfaceId;
658
- auto oldView = mutation.oldChildShadowView;
659
- createLayoutAnimation(mutation, oldView, surfaceId, tag);
660
693
 
661
- Snapshot currentValues(oldView, surfaceManager.getWindow(surfaceId));
662
- Snapshot targetValues(
663
- mutation.newChildShadowView, surfaceManager.getWindow(surfaceId));
694
+ uiScheduler_->scheduleOnUI([this, mutation, surfaceId, tag]() {
695
+ auto oldView = mutation.oldChildShadowView;
696
+ Rect window{};
697
+ {
698
+ auto lock = std::unique_lock<std::recursive_mutex>(mutex);
699
+ createLayoutAnimation(mutation, oldView, surfaceId, tag);
700
+ window = surfaceManager.getWindow(surfaceId);
701
+ }
702
+
703
+ Snapshot currentValues(oldView, window);
704
+ Snapshot targetValues(mutation.newChildShadowView, window);
664
705
 
665
- uiScheduler_->scheduleOnUI([currentValues, targetValues, this, tag]() {
666
706
  jsi::Object yogaValues(uiRuntime_);
667
707
  yogaValues.setProperty(uiRuntime_, "currentOriginX", currentValues.x);
668
708
  yogaValues.setProperty(uiRuntime_, "currentGlobalOriginX", currentValues.x);
@@ -83,6 +83,10 @@ struct Node {
83
83
  : children(std::move(node.children)),
84
84
  unflattenedChildren(std::move(node.unflattenedChildren)),
85
85
  tag(node.tag) {}
86
+ Node(Node &node)
87
+ : children(node.children),
88
+ unflattenedChildren(node.unflattenedChildren),
89
+ tag(node.tag) {}
86
90
  virtual ~Node() = default;
87
91
  };
88
92
 
@@ -195,7 +195,7 @@ void NativeReanimatedModule::scheduleOnUI(
195
195
  const jsi::Value &worklet) {
196
196
  auto shareableWorklet = extractShareableOrThrow<ShareableWorklet>(
197
197
  rt, worklet, "[Reanimated] Only worklets can be scheduled to run on UI.");
198
- uiScheduler_->scheduleOnUI([=] {
198
+ uiScheduler_->scheduleOnUI([=, this] {
199
199
  #if JS_RUNTIME_HERMES
200
200
  // JSI's scope defined here allows for JSI-objects to be cleared up after
201
201
  // each runtime loop. Within these loops we typically create some temporary
@@ -261,7 +261,7 @@ jsi::Value NativeReanimatedModule::registerEventHandler(
261
261
  rt, worklet, "[Reanimated] Event handler must be a worklet.");
262
262
  int emitterReactTagInt = emitterReactTag.asNumber();
263
263
 
264
- uiScheduler_->scheduleOnUI([=] {
264
+ uiScheduler_->scheduleOnUI([=, this] {
265
265
  auto handler = std::make_shared<WorkletEventHandler>(
266
266
  newRegistrationId, eventNameStr, emitterReactTagInt, handlerShareable);
267
267
  eventHandlerRegistry_->registerEventHandler(std::move(handler));
@@ -275,7 +275,7 @@ void NativeReanimatedModule::unregisterEventHandler(
275
275
  const jsi::Value &registrationId) {
276
276
  uint64_t id = registrationId.asNumber();
277
277
  uiScheduler_->scheduleOnUI(
278
- [=] { eventHandlerRegistry_->unregisterEventHandler(id); });
278
+ [=, this] { eventHandlerRegistry_->unregisterEventHandler(id); });
279
279
  }
280
280
 
281
281
  #ifdef RCT_NEW_ARCH_ENABLED
@@ -371,7 +371,7 @@ jsi::Value NativeReanimatedModule::getViewProp(
371
371
 
372
372
  const int viewTagInt = viewTag.asNumber();
373
373
 
374
- uiScheduler_->scheduleOnUI([=]() {
374
+ uiScheduler_->scheduleOnUI([=, this]() {
375
375
  jsi::Runtime &uiRuntime = uiWorkletRuntime_->getJSIRuntime();
376
376
  const jsi::Value propNameValue =
377
377
  jsi::String::createFromUtf8(uiRuntime, propNameStr);
@@ -644,8 +644,9 @@ void NativeReanimatedModule::performOperations() {
644
644
  {
645
645
  auto lock = propsRegistry_->createLock();
646
646
 
647
- if (copiedOperationsQueue.size() > 0) {
648
- propsRegistry_->resetReanimatedSkipCommitFlag();
647
+ if (copiedOperationsQueue.size() > 0 &&
648
+ propsRegistry_->shouldReanimatedSkipCommit()) {
649
+ propsRegistry_->pleaseCommitAfterPause();
649
650
  }
650
651
 
651
652
  // remove recently unmounted ShadowNodes from PropsRegistry
@@ -722,15 +723,9 @@ void NativeReanimatedModule::performOperations() {
722
723
  react_native_assert(family->getSurfaceId() == surfaceId_);
723
724
  propsMap[family].emplace_back(rt, std::move(*props));
724
725
 
725
- #if REACT_NATIVE_MINOR_VERSION >= 73
726
- // Fix for catching nullptr returned from commit hook was
727
- // introduced in 0.72.4 but we have only check for minor version
728
- // of React Native so enable that optimization in React Native >=
729
- // 0.73
730
726
  if (propsRegistry_->shouldReanimatedSkipCommit()) {
731
727
  return nullptr;
732
728
  }
733
- #endif
734
729
  }
735
730
 
736
731
  auto rootNode =
@@ -746,15 +741,12 @@ void NativeReanimatedModule::performOperations() {
746
741
 
747
742
  return rootNode;
748
743
  },
749
- { /* .enableStateReconciliation = */
750
- false,
751
- #if REACT_NATIVE_MINOR_VERSION >= 72
752
- /* .mountSynchronously = */ true,
753
- #endif
754
- /* .shouldYield = */ [this]() {
755
- return propsRegistry_->shouldReanimatedSkipCommit();
756
- }
757
- });
744
+ {/* .enableStateReconciliation = */
745
+ false,
746
+ /* .mountSynchronously = */ true,
747
+ /* .shouldYield = */ [this]() {
748
+ return propsRegistry_->shouldReanimatedSkipCommit();
749
+ }});
758
750
  });
759
751
  }
760
752
 
@@ -840,6 +832,8 @@ void NativeReanimatedModule::initializeFabric(
840
832
 
841
833
  initializeLayoutAnimations();
842
834
 
835
+ mountHook_ =
836
+ std::make_shared<ReanimatedMountHook>(propsRegistry_, uiManager_);
843
837
  commitHook_ =
844
838
  std::make_shared<ReanimatedCommitHook>(propsRegistry_, uiManager_);
845
839
  }
@@ -878,7 +872,7 @@ jsi::Value NativeReanimatedModule::subscribeForKeyboardEvents(
878
872
  handlerWorklet,
879
873
  "[Reanimated] Keyboard event handler must be a worklet.");
880
874
  return subscribeForKeyboardEventsFunction_(
881
- [=](int keyboardState, int height) {
875
+ [=, this](int keyboardState, int height) {
882
876
  uiWorkletRuntime_->runGuarded(
883
877
  shareableHandler, jsi::Value(keyboardState), jsi::Value(height));
884
878
  },
@@ -23,6 +23,7 @@
23
23
  #include "LayoutAnimationsProxy.h"
24
24
  #include "PropsRegistry.h"
25
25
  #include "ReanimatedCommitHook.h"
26
+ #include "ReanimatedMountHook.h"
26
27
  #endif
27
28
 
28
29
  namespace reanimated {
@@ -218,6 +219,7 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec {
218
219
 
219
220
  std::shared_ptr<PropsRegistry> propsRegistry_;
220
221
  std::shared_ptr<ReanimatedCommitHook> commitHook_;
222
+ std::shared_ptr<ReanimatedMountHook> mountHook_;
221
223
 
222
224
  std::vector<Tag> tagsToRemove_; // from `propsRegistry_`
223
225
  #else
@@ -36,11 +36,45 @@ export function setupCallGuard() {
36
36
  };
37
37
  }
38
38
 
39
+ /**
40
+ * Currently there seems to be a bug in the JSI layer which causes a crash when
41
+ * we try to copy some of the console methods, i.e. `clear` or `dirxml`.
42
+ *
43
+ * The crash happens only in React Native 0.75. It's not reproducible in neither
44
+ * 0.76 nor 0.74. It also happens only in the configuration of a debug app and
45
+ * production bundle.
46
+ *
47
+ * I haven't yet discovered what exactly causes the crash. It's tied to the
48
+ * console methods sometimes being `HostFunction`s. Therefore, as a workaround
49
+ * we don't copy the methods as they are in the original console object, we copy
50
+ * JavaScript wrappers instead.
51
+ */
52
+ function createMemorySafeCapturableConsole() {
53
+ const consoleCopy = Object.fromEntries(Object.entries(console).map(([methodName, method]) => {
54
+ const methodWrapper = function methodWrapper(...args) {
55
+ return method(...args);
56
+ };
57
+ if (method.name) {
58
+ /**
59
+ * Set the original method name as the wrapper name if available.
60
+ *
61
+ * It might be unnecessary but if we want to fully mimic the console
62
+ * object we should take into the account the fact some code might rely
63
+ * on the method name.
64
+ */
65
+ Object.defineProperty(methodWrapper, 'name', {
66
+ value: method.name,
67
+ writable: false
68
+ });
69
+ }
70
+ return [methodName, methodWrapper];
71
+ }));
72
+ return consoleCopy;
73
+ }
74
+
39
75
  // We really have to create a copy of console here. Function runOnJS we use on elements inside
40
76
  // this object makes it not configurable
41
- const capturableConsole = {
42
- ...console
43
- };
77
+ const capturableConsole = createMemorySafeCapturableConsole();
44
78
  export function setupConsole() {
45
79
  'worklet';
46
80
 
@@ -1 +1 @@
1
- {"version":3,"names":["reportFatalErrorOnJS","isChromeDebugger","isJest","shouldBeUseWeb","runOnJS","setupMicrotasks","callMicrotasks","runOnUIImmediately","mockedRequestAnimationFrame","IS_JEST","SHOULD_BE_USE_WEB","IS_CHROME_DEBUGGER","callGuardDEV","fn","args","e","global","__ErrorUtils","reportFatalError","setupCallGuard","__callGuardDEV","error","message","stack","capturableConsole","console","setupConsole","assert","debug","log","warn","info","setupRequestAnimationFrame","nativeRequestAnimationFrame","requestAnimationFrame","animationFrameCallbacks","flushRequested","__flushAnimationFrame","frameTimestamp","currentCallbacks","forEach","f","callback","push","timestamp","__frameTimestamp","undefined","initializeUIRuntime","globalThis"],"sources":["initializers.ts"],"sourcesContent":["'use strict';\nimport { reportFatalErrorOnJS } from './errors';\nimport { isChromeDebugger, isJest, shouldBeUseWeb } from './PlatformChecker';\nimport {\n runOnJS,\n setupMicrotasks,\n callMicrotasks,\n runOnUIImmediately,\n} from './threads';\nimport { mockedRequestAnimationFrame } from './mockedRequestAnimationFrame';\n\nconst IS_JEST = isJest();\nconst SHOULD_BE_USE_WEB = shouldBeUseWeb();\nconst IS_CHROME_DEBUGGER = isChromeDebugger();\n\n// callGuard is only used with debug builds\nexport function callGuardDEV<Args extends unknown[], ReturnValue>(\n fn: (...args: Args) => ReturnValue,\n ...args: Args\n): ReturnValue | void {\n 'worklet';\n try {\n return fn(...args);\n } catch (e) {\n if (global.__ErrorUtils) {\n global.__ErrorUtils.reportFatalError(e as Error);\n } else {\n throw e;\n }\n }\n}\n\nexport function setupCallGuard() {\n 'worklet';\n global.__callGuardDEV = callGuardDEV;\n global.__ErrorUtils = {\n reportFatalError: (error: Error) => {\n runOnJS(reportFatalErrorOnJS)({\n message: error.message,\n stack: error.stack,\n });\n },\n };\n}\n\n// We really have to create a copy of console here. Function runOnJS we use on elements inside\n// this object makes it not configurable\nconst capturableConsole = { ...console };\n\nexport function setupConsole() {\n 'worklet';\n if (!IS_CHROME_DEBUGGER) {\n // @ts-ignore TypeScript doesn't like that there are missing methods in console object, but we don't provide all the methods for the UI runtime console version\n global.console = {\n /* eslint-disable @typescript-eslint/unbound-method */\n assert: runOnJS(capturableConsole.assert),\n debug: runOnJS(capturableConsole.debug),\n log: runOnJS(capturableConsole.log),\n warn: runOnJS(capturableConsole.warn),\n error: runOnJS(capturableConsole.error),\n info: runOnJS(capturableConsole.info),\n /* eslint-enable @typescript-eslint/unbound-method */\n };\n }\n}\n\nfunction setupRequestAnimationFrame() {\n 'worklet';\n\n // Jest mocks requestAnimationFrame API and it does not like if that mock gets overridden\n // so we avoid doing requestAnimationFrame batching in Jest environment.\n const nativeRequestAnimationFrame = global.requestAnimationFrame;\n\n let animationFrameCallbacks: Array<(timestamp: number) => void> = [];\n let flushRequested = false;\n\n global.__flushAnimationFrame = (frameTimestamp: number) => {\n const currentCallbacks = animationFrameCallbacks;\n animationFrameCallbacks = [];\n currentCallbacks.forEach((f) => f(frameTimestamp));\n callMicrotasks();\n };\n\n global.requestAnimationFrame = (\n callback: (timestamp: number) => void\n ): number => {\n animationFrameCallbacks.push(callback);\n if (!flushRequested) {\n flushRequested = true;\n nativeRequestAnimationFrame((timestamp) => {\n flushRequested = false;\n global.__frameTimestamp = timestamp;\n global.__flushAnimationFrame(timestamp);\n global.__frameTimestamp = undefined;\n });\n }\n // Reanimated currently does not support cancelling callbacks requested with\n // requestAnimationFrame. We return -1 as identifier which isn't in line\n // with the spec but it should give users better clue in case they actually\n // attempt to store the value returned from rAF and use it for cancelling.\n return -1;\n };\n}\n\nexport function initializeUIRuntime() {\n if (IS_JEST) {\n // requestAnimationFrame react-native jest's setup is incorrect as it polyfills\n // the method directly using setTimeout, therefore the callback doesn't get the\n // expected timestamp as the only argument: https://github.com/facebook/react-native/blob/main/packages/react-native/jest/setup.js#L28\n // We override this setup here to make sure that callbacks get the proper timestamps\n // when executed. For non-jest environments we define requestAnimationFrame in setupRequestAnimationFrame\n // @ts-ignore TypeScript uses Node definition for rAF, setTimeout, etc which returns a Timeout object rather than a number\n globalThis.requestAnimationFrame = mockedRequestAnimationFrame;\n }\n\n runOnUIImmediately(() => {\n 'worklet';\n setupCallGuard();\n setupConsole();\n if (!SHOULD_BE_USE_WEB) {\n setupMicrotasks();\n setupRequestAnimationFrame();\n }\n })();\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["reportFatalErrorOnJS","isChromeDebugger","isJest","shouldBeUseWeb","runOnJS","setupMicrotasks","callMicrotasks","runOnUIImmediately","mockedRequestAnimationFrame","IS_JEST","SHOULD_BE_USE_WEB","IS_CHROME_DEBUGGER","callGuardDEV","fn","args","e","global","__ErrorUtils","reportFatalError","setupCallGuard","__callGuardDEV","error","message","stack","createMemorySafeCapturableConsole","consoleCopy","Object","fromEntries","entries","console","map","methodName","method","methodWrapper","name","defineProperty","value","writable","capturableConsole","setupConsole","assert","debug","log","warn","info","setupRequestAnimationFrame","nativeRequestAnimationFrame","requestAnimationFrame","animationFrameCallbacks","flushRequested","__flushAnimationFrame","frameTimestamp","currentCallbacks","forEach","f","callback","push","timestamp","__frameTimestamp","undefined","initializeUIRuntime","globalThis"],"sources":["initializers.ts"],"sourcesContent":["'use strict';\nimport { reportFatalErrorOnJS } from './errors';\nimport { isChromeDebugger, isJest, shouldBeUseWeb } from './PlatformChecker';\nimport {\n runOnJS,\n setupMicrotasks,\n callMicrotasks,\n runOnUIImmediately,\n} from './threads';\nimport { mockedRequestAnimationFrame } from './mockedRequestAnimationFrame';\n\nconst IS_JEST = isJest();\nconst SHOULD_BE_USE_WEB = shouldBeUseWeb();\nconst IS_CHROME_DEBUGGER = isChromeDebugger();\n\n// callGuard is only used with debug builds\nexport function callGuardDEV<Args extends unknown[], ReturnValue>(\n fn: (...args: Args) => ReturnValue,\n ...args: Args\n): ReturnValue | void {\n 'worklet';\n try {\n return fn(...args);\n } catch (e) {\n if (global.__ErrorUtils) {\n global.__ErrorUtils.reportFatalError(e as Error);\n } else {\n throw e;\n }\n }\n}\n\nexport function setupCallGuard() {\n 'worklet';\n global.__callGuardDEV = callGuardDEV;\n global.__ErrorUtils = {\n reportFatalError: (error: Error) => {\n runOnJS(reportFatalErrorOnJS)({\n message: error.message,\n stack: error.stack,\n });\n },\n };\n}\n\n/**\n * Currently there seems to be a bug in the JSI layer which causes a crash when\n * we try to copy some of the console methods, i.e. `clear` or `dirxml`.\n *\n * The crash happens only in React Native 0.75. It's not reproducible in neither\n * 0.76 nor 0.74. It also happens only in the configuration of a debug app and\n * production bundle.\n *\n * I haven't yet discovered what exactly causes the crash. It's tied to the\n * console methods sometimes being `HostFunction`s. Therefore, as a workaround\n * we don't copy the methods as they are in the original console object, we copy\n * JavaScript wrappers instead.\n */\nfunction createMemorySafeCapturableConsole(): typeof console {\n const consoleCopy = Object.fromEntries(\n Object.entries(console).map(([methodName, method]) => {\n const methodWrapper = function methodWrapper(...args: unknown[]) {\n return method(...args);\n };\n if (method.name) {\n /**\n * Set the original method name as the wrapper name if available.\n *\n * It might be unnecessary but if we want to fully mimic the console\n * object we should take into the account the fact some code might rely\n * on the method name.\n */\n Object.defineProperty(methodWrapper, 'name', {\n value: method.name,\n writable: false,\n });\n }\n return [methodName, methodWrapper];\n })\n );\n\n return consoleCopy as unknown as typeof console;\n}\n\n// We really have to create a copy of console here. Function runOnJS we use on elements inside\n// this object makes it not configurable\nconst capturableConsole = createMemorySafeCapturableConsole();\n\nexport function setupConsole() {\n 'worklet';\n if (!IS_CHROME_DEBUGGER) {\n // @ts-ignore TypeScript doesn't like that there are missing methods in console object, but we don't provide all the methods for the UI runtime console version\n global.console = {\n /* eslint-disable @typescript-eslint/unbound-method */\n assert: runOnJS(capturableConsole.assert),\n debug: runOnJS(capturableConsole.debug),\n log: runOnJS(capturableConsole.log),\n warn: runOnJS(capturableConsole.warn),\n error: runOnJS(capturableConsole.error),\n info: runOnJS(capturableConsole.info),\n /* eslint-enable @typescript-eslint/unbound-method */\n };\n }\n}\n\nfunction setupRequestAnimationFrame() {\n 'worklet';\n\n // Jest mocks requestAnimationFrame API and it does not like if that mock gets overridden\n // so we avoid doing requestAnimationFrame batching in Jest environment.\n const nativeRequestAnimationFrame = global.requestAnimationFrame;\n\n let animationFrameCallbacks: Array<(timestamp: number) => void> = [];\n let flushRequested = false;\n\n global.__flushAnimationFrame = (frameTimestamp: number) => {\n const currentCallbacks = animationFrameCallbacks;\n animationFrameCallbacks = [];\n currentCallbacks.forEach((f) => f(frameTimestamp));\n callMicrotasks();\n };\n\n global.requestAnimationFrame = (\n callback: (timestamp: number) => void\n ): number => {\n animationFrameCallbacks.push(callback);\n if (!flushRequested) {\n flushRequested = true;\n nativeRequestAnimationFrame((timestamp) => {\n flushRequested = false;\n global.__frameTimestamp = timestamp;\n global.__flushAnimationFrame(timestamp);\n global.__frameTimestamp = undefined;\n });\n }\n // Reanimated currently does not support cancelling callbacks requested with\n // requestAnimationFrame. We return -1 as identifier which isn't in line\n // with the spec but it should give users better clue in case they actually\n // attempt to store the value returned from rAF and use it for cancelling.\n return -1;\n };\n}\n\nexport function initializeUIRuntime() {\n if (IS_JEST) {\n // requestAnimationFrame react-native jest's setup is incorrect as it polyfills\n // the method directly using setTimeout, therefore the callback doesn't get the\n // expected timestamp as the only argument: https://github.com/facebook/react-native/blob/main/packages/react-native/jest/setup.js#L28\n // We override this setup here to make sure that callbacks get the proper timestamps\n // when executed. For non-jest environments we define requestAnimationFrame in setupRequestAnimationFrame\n // @ts-ignore TypeScript uses Node definition for rAF, setTimeout, etc which returns a Timeout object rather than a number\n globalThis.requestAnimationFrame = mockedRequestAnimationFrame;\n }\n\n runOnUIImmediately(() => {\n 'worklet';\n setupCallGuard();\n setupConsole();\n if (!SHOULD_BE_USE_WEB) {\n setupMicrotasks();\n setupRequestAnimationFrame();\n }\n })();\n}\n"],"mappings":"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","ignoreList":[]}
@@ -5,5 +5,5 @@
5
5
  * with the version used to build the native part of the library in runtime.
6
6
  * Remember to keep this in sync with the version declared in `package.json`
7
7
  */
8
- export const jsVersion = '3.15.1';
8
+ export const jsVersion = '3.15.3';
9
9
  //# sourceMappingURL=jsVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["jsVersion"],"sources":["jsVersion.ts"],"sourcesContent":["'use strict';\n/**\n * We hardcode the version of Reanimated here in order to compare it\n * with the version used to build the native part of the library in runtime.\n * Remember to keep this in sync with the version declared in `package.json`\n */\nexport const jsVersion = '3.15.1';\n"],"mappings":"AAAA,YAAY;;AACZ;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,SAAS,GAAG,QAAQ","ignoreList":[]}
1
+ {"version":3,"names":["jsVersion"],"sources":["jsVersion.ts"],"sourcesContent":["'use strict';\n/**\n * We hardcode the version of Reanimated here in order to compare it\n * with the version used to build the native part of the library in runtime.\n * Remember to keep this in sync with the version declared in `package.json`\n */\nexport const jsVersion = '3.15.3';\n"],"mappings":"AAAA,YAAY;;AACZ;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,SAAS,GAAG,QAAQ","ignoreList":[]}
@@ -3,4 +3,4 @@
3
3
  * with the version used to build the native part of the library in runtime.
4
4
  * Remember to keep this in sync with the version declared in `package.json`
5
5
  */
6
- export declare const jsVersion = "3.15.1";
6
+ export declare const jsVersion = "3.15.3";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-reanimated",
3
- "version": "3.15.1",
3
+ "version": "3.15.3",
4
4
  "description": "More powerful alternative to Animated library for React Native.",
5
5
  "scripts": {
6
6
  "test": "yarn format:js && yarn lint:js && yarn test:unit",
@@ -43,9 +43,48 @@ export function setupCallGuard() {
43
43
  };
44
44
  }
45
45
 
46
+ /**
47
+ * Currently there seems to be a bug in the JSI layer which causes a crash when
48
+ * we try to copy some of the console methods, i.e. `clear` or `dirxml`.
49
+ *
50
+ * The crash happens only in React Native 0.75. It's not reproducible in neither
51
+ * 0.76 nor 0.74. It also happens only in the configuration of a debug app and
52
+ * production bundle.
53
+ *
54
+ * I haven't yet discovered what exactly causes the crash. It's tied to the
55
+ * console methods sometimes being `HostFunction`s. Therefore, as a workaround
56
+ * we don't copy the methods as they are in the original console object, we copy
57
+ * JavaScript wrappers instead.
58
+ */
59
+ function createMemorySafeCapturableConsole(): typeof console {
60
+ const consoleCopy = Object.fromEntries(
61
+ Object.entries(console).map(([methodName, method]) => {
62
+ const methodWrapper = function methodWrapper(...args: unknown[]) {
63
+ return method(...args);
64
+ };
65
+ if (method.name) {
66
+ /**
67
+ * Set the original method name as the wrapper name if available.
68
+ *
69
+ * It might be unnecessary but if we want to fully mimic the console
70
+ * object we should take into the account the fact some code might rely
71
+ * on the method name.
72
+ */
73
+ Object.defineProperty(methodWrapper, 'name', {
74
+ value: method.name,
75
+ writable: false,
76
+ });
77
+ }
78
+ return [methodName, methodWrapper];
79
+ })
80
+ );
81
+
82
+ return consoleCopy as unknown as typeof console;
83
+ }
84
+
46
85
  // We really have to create a copy of console here. Function runOnJS we use on elements inside
47
86
  // this object makes it not configurable
48
- const capturableConsole = { ...console };
87
+ const capturableConsole = createMemorySafeCapturableConsole();
49
88
 
50
89
  export function setupConsole() {
51
90
  'worklet';
@@ -4,4 +4,4 @@
4
4
  * with the version used to build the native part of the library in runtime.
5
5
  * Remember to keep this in sync with the version declared in `package.json`
6
6
  */
7
- export const jsVersion = '3.15.1';
7
+ export const jsVersion = '3.15.3';