@@ -38,6 +38,14 @@ std::string getShadowTreeCommitSourceName(ShadowTreeCommitSource source) {
3838 return " ReactRevisionMerge" ;
3939 }
4040}
41+
42+ inline bool isPropsUpdatesAccumulationGuaranteed () {
43+ #ifdef __ANDROID__
44+ return ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid ();
45+ #else
46+ return true ;
47+ #endif
48+ }
4149} // namespace
4250
4351using CommitStatus = ShadowTree::CommitStatus;
@@ -482,31 +490,63 @@ void ShadowTree::mount(ShadowTreeRevision revision, bool mountSynchronously)
482490
483491void ShadowTree::mergeReactRevision () const {
484492 ShadowTreeRevision promotedRevision;
493+ std::vector<ShadowTreeRevision> promotedRevisions;
494+ // If props updates accumulation is guaranteed, we can merge the promoted
495+ // react revision, otherwise we need to merge all queued revisions.
485496
486497 {
487498 UniqueLock lock = uniqueRevisionLock (false );
488499
489- if (!reactRevisionToBePromoted_.has_value ()) {
490- return ;
491- }
500+ if (isPropsUpdatesAccumulationGuaranteed ()) {
501+ if (!reactRevisionToBePromoted_.has_value ()) {
502+ return ;
503+ }
492504
493- promotedRevision =
494- std::exchange (reactRevisionToBePromoted_, std::nullopt ).value ();
495- }
505+ promotedRevision =
506+ std::exchange (reactRevisionToBePromoted_, std::nullopt ).value ();
507+ } else {
508+ if (promotedReactRevisions_.empty ()) {
509+ return ;
510+ }
496511
497- const auto mergedRevisionNumber = promotedRevision.number ;
512+ promotedReactRevisions_.swap (promotedRevisions);
513+ }
514+ }
498515
499- this ->commit (
500- [revision = std::move (promotedRevision)](
501- const RootShadowNode& /* oldRootShadowNode*/ ) {
502- return std::make_shared<RootShadowNode>(
503- *revision.rootShadowNode , ShadowNodeFragment{});
504- },
505- {
506- .enableStateReconciliation = true ,
507- .mountSynchronously = true ,
508- .source = CommitSource::ReactRevisionMerge,
509- });
516+ ShadowTreeRevision::Number lastMergedRevisionNumber;
517+
518+ if (isPropsUpdatesAccumulationGuaranteed ()) {
519+ lastMergedRevisionNumber = promotedRevision.number ;
520+ this ->commit (
521+ [revision = std::move (promotedRevision)](
522+ const RootShadowNode& /* oldRootShadowNode*/ ) {
523+ return std::make_shared<RootShadowNode>(
524+ *revision.rootShadowNode , ShadowNodeFragment{});
525+ },
526+ {
527+ .enableStateReconciliation = true ,
528+ .mountSynchronously = true ,
529+ .source = CommitSource::ReactRevisionMerge,
530+ });
531+ } else {
532+ for (size_t i = 0 ; i < promotedRevisions.size (); ++i) {
533+ auto & revision = promotedRevisions[i];
534+ bool isLast = i == promotedRevisions.size () - 1 ;
535+ lastMergedRevisionNumber = revision.number ;
536+
537+ this ->commit (
538+ [revision = std::move (revision)](
539+ const RootShadowNode& /* oldRootShadowNode*/ ) {
540+ return std::make_shared<RootShadowNode>(
541+ *revision.rootShadowNode , ShadowNodeFragment{});
542+ },
543+ {
544+ .enableStateReconciliation = isLast,
545+ .mountSynchronously = true ,
546+ .source = CommitSource::ReactRevisionMerge,
547+ });
548+ }
549+ }
510550
511551 {
512552 UniqueLock commitLock = uniqueRevisionLock (
@@ -515,34 +555,60 @@ void ShadowTree::mergeReactRevision() const {
515555 // If the current react revision is the same as the one that was just
516556 // merged, clear it.
517557 if (currentReactRevision_.has_value () &&
518- mergedRevisionNumber == currentReactRevision_.value ().number ) {
558+ lastMergedRevisionNumber == currentReactRevision_.value ().number ) {
519559 currentReactRevision_.reset ();
520560 }
521561 }
522562}
523563
524564void ShadowTree::promoteReactRevision () const {
525- ShadowTreeRevision currentReactRevision;
526- {
527- SharedLock lock = sharedRevisionLock ();
528- // Promotion happens at the end of the event loop tick, it's possible to
529- // have more than one promotion in a row. In this case, all but the first
530- // one should no-op.
531- if (!currentReactRevision_.has_value ()) {
532- return ;
565+ // Promote only when props updates accumulation is guaranteed. Otherwise,
566+ // queuedReactRevisions_ will be used instead.
567+ if (isPropsUpdatesAccumulationGuaranteed ()) {
568+ ShadowTreeRevision currentReactRevision;
569+ {
570+ SharedLock lock = sharedRevisionLock ();
571+ // Promotion happens at the end of the event loop tick, it's possible to
572+ // have more than one promotion in a row. In this case, all but the first
573+ // one should no-op.
574+ if (!currentReactRevision_.has_value ()) {
575+ return ;
576+ }
577+ currentReactRevision = currentReactRevision_.value ();
533578 }
534- currentReactRevision = currentReactRevision_.value ();
535- }
536579
537- {
580+ {
581+ UniqueLock lock = uniqueRevisionLock (false );
582+ reactRevisionToBePromoted_ = std::move (currentReactRevision);
583+ }
584+ } else {
538585 UniqueLock lock = uniqueRevisionLock (false );
539- reactRevisionToBePromoted_ = std::move (currentReactRevision);
586+
587+ if (queuedReactRevisions_.empty ()) {
588+ return ;
589+ }
590+
591+ // Move all queued revisions to the promoted revisions.
592+ promotedReactRevisions_.insert (
593+ promotedReactRevisions_.end (),
594+ std::make_move_iterator (queuedReactRevisions_.begin ()),
595+ std::make_move_iterator (queuedReactRevisions_.end ()));
596+ queuedReactRevisions_.clear ();
540597 }
541598
542599 delegate_.shadowTreeDidPromoteReactRevision (*this );
543600}
544601
545602void ShadowTree::scheduleReactRevisionPromotion () const {
603+ if (!isPropsUpdatesAccumulationGuaranteed ()) {
604+ // If props updates accumulation is not guaranteed, we need to store each
605+ // revision to be merged separately.
606+ UniqueLock lock = uniqueRevisionLock (false );
607+ if (currentReactRevision_.has_value ()) {
608+ queuedReactRevisions_.push_back (currentReactRevision_.value ());
609+ }
610+ }
611+
546612 delegate_.shadowTreeDidFinishReactCommit (*this );
547613}
548614
0 commit comments