Fabric: Using ShadowView instead of ShadowNode in Mutations

Summary:
@public
This is quite a big diff but the actual meaningful change is simple: now we use ShadowView class instead of ShadowNode in mutation instructions.
Note:
 * In some places (especially during diffing) we have to operate with ShadowNodeViewPair objects (which represents a pair of ShadowNode and ShadowView). The reason for that is that we cannot construct child ShadowViews from parent ShadowViews because they don't have any information about children.
 * `ShadowTree::emitLayoutEvents` is now much simpler because ShadowView better represents the specifics of this kind of object.
 * The code in RCTMountingManager also became simpler.

This change will allow us to implement more cool tricks soon.

Reviewed By: mdvacca

Differential Revision: D9403564

fbshipit-source-id: dbc7c61af250144d6c7335a01dc30df0005559a2
This commit is contained in:
Valentin Shergin 2018-09-03 22:53:18 -07:00 committed by Facebook Github Bot
parent 5c83855c75
commit 0792fba63f
17 changed files with 266 additions and 598 deletions

View File

@ -7,7 +7,8 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <fabric/uimanager/TreeMutationInstruction.h> #import <fabric/uimanager/ShadowView.h>
#import <fabric/uimanager/ShadowViewMutation.h>
#import <React/RCTPrimitives.h> #import <React/RCTPrimitives.h>
#import <React/RCTMountingManagerDelegate.h> #import <React/RCTMountingManagerDelegate.h>
@ -28,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN
* The order of mutation tnstructions matters. * The order of mutation tnstructions matters.
* Can be called from any thread. * Can be called from any thread.
*/ */
- (void)mutateComponentViewTreeWithMutationInstructions:(facebook::react::TreeMutationInstructionList)instructions - (void)performTransactionWithMutations:(facebook::react::ShadowViewMutationList)mutations
rootTag:(ReactTag)rootTag; rootTag:(ReactTag)rootTag;
/** /**

View File

@ -38,124 +38,113 @@ using namespace facebook::react;
return self; return self;
} }
- (void)mutateComponentViewTreeWithMutationInstructions:(facebook::react::TreeMutationInstructionList)instructions - (void)performTransactionWithMutations:(facebook::react::ShadowViewMutationList)mutations
rootTag:(ReactTag)rootTag rootTag:(ReactTag)rootTag
{ {
NSMutableArray<RCTMountItemProtocol> *mountItems = NSMutableArray<RCTMountItemProtocol> *mountItems =
[[NSMutableArray<RCTMountItemProtocol> alloc] initWithCapacity:instructions.size() * 2 /* ~ the worst case */]; [[NSMutableArray<RCTMountItemProtocol> alloc] initWithCapacity:mutations.size() * 2 /* ~ the worst case */];
for (auto instruction : instructions) { for (const auto &mutation : mutations) {
switch (instruction.getType()) { switch (mutation.type) {
case TreeMutationInstruction::Creation: { case ShadowViewMutation::Create: {
NSString *componentName = RCTNSStringFromString(instruction.getNewChildNode()->getComponentName(), NSASCIIStringEncoding); NSString *componentName = RCTNSStringFromString(mutation.newChildShadowView.componentName, NSASCIIStringEncoding);
RCTCreateMountItem *mountItem = RCTCreateMountItem *mountItem =
[[RCTCreateMountItem alloc] initWithComponentName:componentName [[RCTCreateMountItem alloc] initWithComponentName:componentName
tag:instruction.getNewChildNode()->getTag()]; tag:mutation.newChildShadowView.tag];
[mountItems addObject:mountItem]; [mountItems addObject:mountItem];
break; break;
} }
case TreeMutationInstruction::Deletion: { case ShadowViewMutation::Delete: {
NSString *componentName = RCTNSStringFromString(instruction.getOldChildNode()->getComponentName(), NSASCIIStringEncoding); NSString *componentName = RCTNSStringFromString(mutation.oldChildShadowView.componentName, NSASCIIStringEncoding);
RCTDeleteMountItem *mountItem = RCTDeleteMountItem *mountItem =
[[RCTDeleteMountItem alloc] initWithComponentName:componentName [[RCTDeleteMountItem alloc] initWithComponentName:componentName
tag:instruction.getOldChildNode()->getTag()]; tag:mutation.oldChildShadowView.tag];
[mountItems addObject:mountItem]; [mountItems addObject:mountItem];
break; break;
} }
case TreeMutationInstruction::Insertion: { case ShadowViewMutation::Insert: {
// Props // Props
[mountItems addObject:[[RCTUpdatePropsMountItem alloc] initWithTag:instruction.getNewChildNode()->getTag() [mountItems addObject:[[RCTUpdatePropsMountItem alloc] initWithTag:mutation.newChildShadowView.tag
oldProps:nullptr oldProps:nullptr
newProps:instruction.getNewChildNode()->getProps()]]; newProps:mutation.newChildShadowView.props]];
// EventEmitter // EventEmitter
[mountItems addObject:[[RCTUpdateEventEmitterMountItem alloc] initWithTag:instruction.getNewChildNode()->getTag() [mountItems addObject:[[RCTUpdateEventEmitterMountItem alloc] initWithTag:mutation.newChildShadowView.tag
eventEmitter:instruction.getNewChildNode()->getEventEmitter()]]; eventEmitter:mutation.newChildShadowView.eventEmitter]];
// LocalData // LocalData
if (instruction.getNewChildNode()->getLocalData()) { if (mutation.newChildShadowView.localData) {
[mountItems addObject:[[RCTUpdateLocalDataMountItem alloc] initWithTag:instruction.getNewChildNode()->getTag() [mountItems addObject:[[RCTUpdateLocalDataMountItem alloc] initWithTag:mutation.newChildShadowView.tag
oldLocalData:nullptr oldLocalData:nullptr
newLocalData:instruction.getNewChildNode()->getLocalData()]]; newLocalData:mutation.newChildShadowView.localData]];
} }
// Layout // Layout
auto layoutableNewShadowNode = if (mutation.newChildShadowView.layoutMetrics != EmptyLayoutMetrics) {
std::dynamic_pointer_cast<const LayoutableShadowNode>(instruction.getNewChildNode()); [mountItems addObject:[[RCTUpdateLayoutMetricsMountItem alloc] initWithTag:mutation.newChildShadowView.tag
if (layoutableNewShadowNode) {
[mountItems addObject:[[RCTUpdateLayoutMetricsMountItem alloc] initWithTag:instruction.getNewChildNode()->getTag()
oldLayoutMetrics:{} oldLayoutMetrics:{}
newLayoutMetrics:layoutableNewShadowNode->getLayoutMetrics()]]; newLayoutMetrics:mutation.newChildShadowView.layoutMetrics]];
} }
// Insertion // Insertion
RCTInsertMountItem *mountItem = RCTInsertMountItem *mountItem =
[[RCTInsertMountItem alloc] initWithChildTag:instruction.getNewChildNode()->getTag() [[RCTInsertMountItem alloc] initWithChildTag:mutation.newChildShadowView.tag
parentTag:instruction.getParentNode()->getTag() parentTag:mutation.parentShadowView.tag
index:instruction.getIndex()]; index:mutation.index];
[mountItems addObject:mountItem]; [mountItems addObject:mountItem];
break; break;
} }
case TreeMutationInstruction::Removal: { case ShadowViewMutation::Remove: {
RCTRemoveMountItem *mountItem = RCTRemoveMountItem *mountItem =
[[RCTRemoveMountItem alloc] initWithChildTag:instruction.getOldChildNode()->getTag() [[RCTRemoveMountItem alloc] initWithChildTag:mutation.oldChildShadowView.tag
parentTag:instruction.getParentNode()->getTag() parentTag:mutation.parentShadowView.tag
index:instruction.getIndex()]; index:mutation.index];
[mountItems addObject:mountItem]; [mountItems addObject:mountItem];
break; break;
} }
case TreeMutationInstruction::Replacement: { case ShadowViewMutation::Update: {
SharedShadowNode oldShadowNode = instruction.getOldChildNode(); auto oldChildShadowView = mutation.oldChildShadowView;
SharedShadowNode newShadowNode = instruction.getNewChildNode(); auto newChildShadowView = mutation.newChildShadowView;
// Props // Props
if (oldShadowNode->getProps() != newShadowNode->getProps()) { if (oldChildShadowView.props != newChildShadowView.props) {
RCTUpdatePropsMountItem *mountItem = RCTUpdatePropsMountItem *mountItem =
[[RCTUpdatePropsMountItem alloc] initWithTag:instruction.getOldChildNode()->getTag() [[RCTUpdatePropsMountItem alloc] initWithTag:mutation.oldChildShadowView.tag
oldProps:instruction.getOldChildNode()->getProps() oldProps:mutation.oldChildShadowView.props
newProps:instruction.getNewChildNode()->getProps()]; newProps:mutation.newChildShadowView.props];
[mountItems addObject:mountItem]; [mountItems addObject:mountItem];
} }
// EventEmitter // EventEmitter
if (oldShadowNode->getEventEmitter() != newShadowNode->getEventEmitter()) { if (oldChildShadowView.eventEmitter != newChildShadowView.eventEmitter) {
RCTUpdateEventEmitterMountItem *mountItem = RCTUpdateEventEmitterMountItem *mountItem =
[[RCTUpdateEventEmitterMountItem alloc] initWithTag:instruction.getOldChildNode()->getTag() [[RCTUpdateEventEmitterMountItem alloc] initWithTag:mutation.oldChildShadowView.tag
eventEmitter:instruction.getOldChildNode()->getEventEmitter()]; eventEmitter:mutation.oldChildShadowView.eventEmitter];
[mountItems addObject:mountItem]; [mountItems addObject:mountItem];
} }
// LocalData // LocalData
if (oldShadowNode->getLocalData() != newShadowNode->getLocalData()) { if (oldChildShadowView.localData != newChildShadowView.localData) {
RCTUpdateLocalDataMountItem *mountItem = RCTUpdateLocalDataMountItem *mountItem =
[[RCTUpdateLocalDataMountItem alloc] initWithTag:newShadowNode->getTag() [[RCTUpdateLocalDataMountItem alloc] initWithTag:newChildShadowView.tag
oldLocalData:oldShadowNode->getLocalData() oldLocalData:oldChildShadowView.localData
newLocalData:newShadowNode->getLocalData()]; newLocalData:newChildShadowView.localData];
[mountItems addObject:mountItem]; [mountItems addObject:mountItem];
} }
// Layout // Layout
auto layoutableOldShadowNode = if (oldChildShadowView.layoutMetrics != newChildShadowView.layoutMetrics) {
std::dynamic_pointer_cast<const LayoutableShadowNode>(oldShadowNode);
if (layoutableOldShadowNode) {
auto layoutableNewShadowNode =
std::dynamic_pointer_cast<const LayoutableShadowNode>(newShadowNode);
if (layoutableOldShadowNode->getLayoutMetrics() != layoutableNewShadowNode->getLayoutMetrics()) {
RCTUpdateLayoutMetricsMountItem *mountItem = RCTUpdateLayoutMetricsMountItem *mountItem =
[[RCTUpdateLayoutMetricsMountItem alloc] initWithTag:instruction.getOldChildNode()->getTag() [[RCTUpdateLayoutMetricsMountItem alloc] initWithTag:mutation.oldChildShadowView.tag
oldLayoutMetrics:layoutableOldShadowNode->getLayoutMetrics() oldLayoutMetrics:oldChildShadowView.layoutMetrics
newLayoutMetrics:layoutableNewShadowNode->getLayoutMetrics()]; newLayoutMetrics:newChildShadowView.layoutMetrics];
[mountItems addObject:mountItem]; [mountItems addObject:mountItem];
} }
}
break; break;
} }

View File

@ -12,7 +12,7 @@
#import <fabric/core/LayoutConstraints.h> #import <fabric/core/LayoutConstraints.h>
#import <fabric/core/LayoutContext.h> #import <fabric/core/LayoutContext.h>
#import <fabric/uimanager/FabricUIManager.h> #import <fabric/uimanager/FabricUIManager.h>
#import <fabric/uimanager/TreeMutationInstruction.h> #import <fabric/uimanager/ShadowViewMutation.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -23,7 +23,8 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
@protocol RCTSchedulerDelegate @protocol RCTSchedulerDelegate
- (void)schedulerDidComputeMutationInstructions:(facebook::react::TreeMutationInstructionList)instructions rootTag:(ReactTag)rootTag; - (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList)mutations
rootTag:(ReactTag)rootTag;
- (void)schedulerDidRequestPreliminaryViewAllocationWithComponentName:(NSString *)componentName; - (void)schedulerDidRequestPreliminaryViewAllocationWithComponentName:(NSString *)componentName;

View File

@ -30,9 +30,9 @@ class SchedulerDelegateProxy: public SchedulerDelegate {
public: public:
SchedulerDelegateProxy(void *scheduler): scheduler_(scheduler) {} SchedulerDelegateProxy(void *scheduler): scheduler_(scheduler) {}
void schedulerDidComputeMutationInstructions(Tag rootTag, const TreeMutationInstructionList &instructions) override { void schedulerDidFinishTransaction(Tag rootTag, const ShadowViewMutationList &mutations) override {
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_; RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
[scheduler.delegate schedulerDidComputeMutationInstructions:instructions rootTag:rootTag]; [scheduler.delegate schedulerDidFinishTransaction:mutations rootTag:rootTag];
} }
void schedulerDidRequestPreliminaryViewAllocation(ComponentName componentName) override { void schedulerDidRequestPreliminaryViewAllocation(ComponentName componentName) override {

View File

@ -69,10 +69,10 @@ using namespace facebook::react;
#pragma mark - RCTSchedulerDelegate #pragma mark - RCTSchedulerDelegate
- (void)schedulerDidComputeMutationInstructions:(facebook::react::TreeMutationInstructionList)instructions - (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList)mutations
rootTag:(ReactTag)rootTag rootTag:(ReactTag)rootTag
{ {
[_mountingManager mutateComponentViewTreeWithMutationInstructions:instructions [_mountingManager performTransactionWithMutations:mutations
rootTag:rootTag]; rootTag:rootTag];
} }
@ -89,7 +89,7 @@ using namespace facebook::react;
[_scheduler registerRootTag:surface.rootTag]; [_scheduler registerRootTag:surface.rootTag];
[self runSurface:surface]; [self runSurface:surface];
// FIXME: Mutation instruction MUST produce instruction for root node. // FIXME: mutation MUST produce instruction for root node.
[_mountingManager.componentViewRegistry dequeueComponentViewWithName:@"Root" tag:surface.rootTag]; [_mountingManager.componentViewRegistry dequeueComponentViewWithName:@"Root" tag:surface.rootTag];
} }

View File

@ -106,7 +106,7 @@ public:
/* /*
* Equality operators. * Equality operators.
* Use this to compare `ShadowNode`s values for equality (and non-equality). * Use this to compare `ShadowNode`s values for equality (and non-equality).
* Same values indicates that nodes must not produce mutation instructions * Same values indicates that nodes must not produce mutations
* during tree diffing process. * during tree diffing process.
* Child nodes are not considered as part of the value. * Child nodes are not considered as part of the value.
*/ */

View File

@ -5,202 +5,217 @@
#include "Differentiator.h" #include "Differentiator.h"
#include "ShadowView.h"
#include <fabric/core/LayoutableShadowNode.h>
namespace facebook { namespace facebook {
namespace react { namespace react {
static void calculateMutationInstructions( static ShadowViewNodePairList sliceChildShadowNodeViewPairs(const ShadowNode &shadowNode) {
TreeMutationInstructionList &instructions, ShadowViewNodePairList pairList;
SharedShadowNode parentNode,
const SharedShadowNodeList &oldChildNodes, for (const auto &childShadowNode : shadowNode.getChildren()) {
const SharedShadowNodeList &newChildNodes pairList.push_back({ShadowView(*childShadowNode), *childShadowNode});
}
return pairList;
}
static void calculateShadowViewMutations(
ShadowViewMutationList &mutations,
const ShadowView &parentShadowView,
const ShadowViewNodePairList &oldChildPairs,
const ShadowViewNodePairList &newChildPairs
) { ) {
// The current version of the algorithm is otimized for simplicity, // The current version of the algorithm is otimized for simplicity,
// not for performance of optimal result. // not for performance or optimal result.
// TODO(shergin): Consider to use Minimal Edit Distance algorithm to produce if (oldChildPairs == newChildPairs) {
// optimal set of instructions and improve mounting performance.
// https://en.wikipedia.org/wiki/Edit_distance
// https://www.geeksforgeeks.org/dynamic-programming-set-5-edit-distance/
if (oldChildNodes == newChildNodes) {
return; return;
} }
if (oldChildNodes.size() == 0 && newChildNodes.size() == 0) { if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) {
return; return;
} }
std::unordered_map<Tag, SharedShadowNode> insertedNodes; std::unordered_map<Tag, ShadowViewNodePair> insertedPaires;
int index = 0; int index = 0;
TreeMutationInstructionList createInstructions = {}; ShadowViewMutationList createMutations = {};
TreeMutationInstructionList deleteInstructions = {}; ShadowViewMutationList deleteMutations = {};
TreeMutationInstructionList insertInstructions = {}; ShadowViewMutationList insertMutations = {};
TreeMutationInstructionList removeInstructions = {}; ShadowViewMutationList removeMutations = {};
TreeMutationInstructionList replaceInstructions = {}; ShadowViewMutationList updateMutations = {};
TreeMutationInstructionList downwardInstructions = {}; ShadowViewMutationList downwardMutations = {};
TreeMutationInstructionList destructionDownwardInstructions = {}; ShadowViewMutationList destructiveDownwardMutations = {};
// Stage 1: Collectings Updates // Stage 1: Collecting `Update` mutations
for (index = 0; index < oldChildNodes.size() && index < newChildNodes.size(); index++) { for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size(); index++) {
const auto &oldChildNode = oldChildNodes.at(index); const auto &oldChildPair = oldChildPairs[index];
const auto &newChildNode = newChildNodes.at(index); const auto &newChildPair = newChildPairs[index];
if (oldChildNode->getTag() != newChildNode->getTag()) { if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) {
// Totally different nodes, updating is impossible. // Totally different nodes, updating is impossible.
break; break;
} }
if (*oldChildNode != *newChildNode) { if (oldChildPair.shadowView != newChildPair.shadowView) {
replaceInstructions.push_back( updateMutations.push_back(
TreeMutationInstruction::Replace( ShadowViewMutation::UpdateMutation(
parentNode, parentShadowView,
oldChildNode, oldChildPair.shadowView,
newChildNode, newChildPair.shadowView,
index index
) )
); );
} }
calculateMutationInstructions( const auto oldGrandChildPairs = sliceChildShadowNodeViewPairs(oldChildPair.shadowNode);
*(newChildNode->getChildren().size() ? &downwardInstructions : &destructionDownwardInstructions), const auto newGrandChildPairs = sliceChildShadowNodeViewPairs(newChildPair.shadowNode);
oldChildNode, calculateShadowViewMutations(
oldChildNode->getChildren(), *(newGrandChildPairs.size() ? &downwardMutations : &destructiveDownwardMutations),
newChildNode->getChildren() oldChildPair.shadowView,
oldGrandChildPairs,
newGrandChildPairs
); );
} }
int lastIndexAfterFirstStage = index; int lastIndexAfterFirstStage = index;
// Stage 2: Collectings Insertions // Stage 2: Collecting `Insert` mutations
for (; index < newChildNodes.size(); index++) { for (; index < newChildPairs.size(); index++) {
const auto &newChildNode = newChildNodes.at(index); const auto &newChildPair = newChildPairs[index];
insertInstructions.push_back( insertMutations.push_back(
TreeMutationInstruction::Insert( ShadowViewMutation::InsertMutation(
parentNode, parentShadowView,
newChildNode, newChildPair.shadowView,
index index
) )
); );
insertedNodes.insert({newChildNode->getTag(), newChildNode}); insertedPaires.insert({newChildPair.shadowView.tag, newChildPair});
} }
// Stage 3: Collectings Deletions and Removals // Stage 3: Collecting `Delete` and `Remove` mutations
for (index = lastIndexAfterFirstStage; index < oldChildNodes.size(); index++) { for (index = lastIndexAfterFirstStage; index < oldChildPairs.size(); index++) {
const auto &oldChildNode = oldChildNodes.at(index); const auto &oldChildPair = oldChildPairs[index];
// Even if the old node was (re)inserted, we have to generate `remove` // Even if the old view was (re)inserted, we have to generate `remove`
// instruction. // mutation.
removeInstructions.push_back( removeMutations.push_back(
TreeMutationInstruction::Remove( ShadowViewMutation::RemoveMutation(
parentNode, parentShadowView,
oldChildNode, oldChildPair.shadowView,
index index
) )
); );
const auto &it = insertedNodes.find(oldChildNode->getTag()); const auto &it = insertedPaires.find(oldChildPair.shadowView.tag);
if (it == insertedNodes.end()) { if (it == insertedPaires.end()) {
// The old node was *not* (re)inserted. // The old view was *not* (re)inserted.
// We have to generate `delete` instruction and apply the algorithm // We have to generate `delete` mutation and apply the algorithm
// recursively. // recursively.
deleteInstructions.push_back( deleteMutations.push_back(
TreeMutationInstruction::Delete( ShadowViewMutation::DeleteMutation(
oldChildNode oldChildPair.shadowView
) )
); );
// We also have to call the algorithm recursively to clean up the entire // We also have to call the algorithm recursively to clean up the entire
// subtree starting from the removed node. // subtree starting from the removed view.
calculateMutationInstructions( calculateShadowViewMutations(
destructionDownwardInstructions, destructiveDownwardMutations,
oldChildNode, oldChildPair.shadowView,
oldChildNode->getChildren(), sliceChildShadowNodeViewPairs(oldChildPair.shadowNode),
{} {}
); );
} else { } else {
// The old node *was* (re)inserted. // The old view *was* (re)inserted.
// We have to call the algorithm recursively if the inserted node // We have to call the algorithm recursively if the inserted view
// is *not* the same as removed one. // is *not* the same as removed one.
const auto &newChildNode = it->second; const auto &newChildPair = it->second;
if (newChildNode != oldChildNode) { if (newChildPair.shadowView != oldChildPair.shadowView) {
calculateMutationInstructions( const auto oldGrandChildPairs = sliceChildShadowNodeViewPairs(oldChildPair.shadowNode);
*(newChildNode->getChildren().size() ? &downwardInstructions : &destructionDownwardInstructions), const auto newGrandChildPairs = sliceChildShadowNodeViewPairs(newChildPair.shadowNode);
newChildNode, calculateShadowViewMutations(
oldChildNode->getChildren(), *(newGrandChildPairs.size() ? &downwardMutations : &destructiveDownwardMutations),
newChildNode->getChildren() newChildPair.shadowView,
oldGrandChildPairs,
newGrandChildPairs
); );
} }
// In any case we have to remove the node from `insertedNodes` as // In any case we have to remove the view from `insertedPaires` as
// indication that the node was actually removed (which means that // indication that the view was actually removed (which means that
// the node existed before), hence we don't have to generate // the view existed before), hence we don't have to generate
// `create` instruction. // `create` mutation.
insertedNodes.erase(it); insertedPaires.erase(it);
} }
} }
// Stage 4: Collectings Creations // Stage 4: Collecting `Create` mutations
for (index = lastIndexAfterFirstStage; index < newChildNodes.size(); index++) { for (index = lastIndexAfterFirstStage; index < newChildPairs.size(); index++) {
const auto &newChildNode = newChildNodes.at(index); const auto &newChildPair = newChildPairs[index];
if (insertedNodes.find(newChildNode->getTag()) == insertedNodes.end()) { if (insertedPaires.find(newChildPair.shadowView.tag) == insertedPaires.end()) {
// The new node was (re)inserted, so there is no need to create it. // The new view was (re)inserted, so there is no need to create it.
continue; continue;
} }
createInstructions.push_back( createMutations.push_back(
TreeMutationInstruction::Create( ShadowViewMutation::CreateMutation(
newChildNode newChildPair.shadowView
) )
); );
calculateMutationInstructions( calculateShadowViewMutations(
downwardInstructions, downwardMutations,
newChildNode, newChildPair.shadowView,
{}, {},
newChildNode->getChildren() sliceChildShadowNodeViewPairs(newChildPair.shadowNode)
); );
} }
// All instructions in an optimal order: // All mutations in an optimal order:
instructions.insert(instructions.end(), destructionDownwardInstructions.begin(), destructionDownwardInstructions.end()); mutations.insert(mutations.end(), destructiveDownwardMutations.begin(), destructiveDownwardMutations.end());
instructions.insert(instructions.end(), replaceInstructions.begin(), replaceInstructions.end()); mutations.insert(mutations.end(), updateMutations.begin(), updateMutations.end());
instructions.insert(instructions.end(), removeInstructions.rbegin(), removeInstructions.rend()); mutations.insert(mutations.end(), removeMutations.rbegin(), removeMutations.rend());
instructions.insert(instructions.end(), createInstructions.begin(), createInstructions.end()); mutations.insert(mutations.end(), deleteMutations.begin(), deleteMutations.end());
instructions.insert(instructions.end(), downwardInstructions.begin(), downwardInstructions.end()); mutations.insert(mutations.end(), createMutations.begin(), createMutations.end());
instructions.insert(instructions.end(), insertInstructions.begin(), insertInstructions.end()); mutations.insert(mutations.end(), insertMutations.begin(), insertMutations.end());
instructions.insert(instructions.end(), deleteInstructions.begin(), deleteInstructions.end()); mutations.insert(mutations.end(), downwardMutations.begin(), downwardMutations.end());
} }
void calculateMutationInstructions( ShadowViewMutationList calculateShadowViewMutations(
TreeMutationInstructionList &instructions, const ShadowNode &oldRootShadowNode,
const SharedShadowNode &oldRootShadowNode, const ShadowNode &newRootShadowNode
const SharedShadowNode &newRootShadowNode
) { ) {
// Root shadow nodes must have same tag. // Root shadow nodes must have same tag.
assert(oldRootShadowNode->getTag() == newRootShadowNode->getTag()); assert(oldRootShadowNode.getTag() == newRootShadowNode.getTag());
if (*oldRootShadowNode != *newRootShadowNode) { ShadowViewMutationList mutations;
instructions.push_back(
TreeMutationInstruction::Replace( if (oldRootShadowNode != newRootShadowNode) {
nullptr, mutations.push_back(
oldRootShadowNode, ShadowViewMutation::UpdateMutation(
newRootShadowNode, ShadowView(),
ShadowView(oldRootShadowNode),
ShadowView(newRootShadowNode),
-1 -1
) )
); );
} }
calculateMutationInstructions( calculateShadowViewMutations(
instructions, mutations,
oldRootShadowNode, ShadowView(oldRootShadowNode),
oldRootShadowNode->getChildren(), sliceChildShadowNodeViewPairs(oldRootShadowNode),
newRootShadowNode->getChildren() sliceChildShadowNodeViewPairs(newRootShadowNode)
); );
return mutations;
} }
} // namespace react } // namespace react

View File

@ -6,20 +6,19 @@
#pragma once #pragma once
#include <fabric/core/ShadowNode.h> #include <fabric/core/ShadowNode.h>
#include <fabric/uimanager/TreeMutationInstruction.h> #include <fabric/uimanager/ShadowViewMutation.h>
namespace facebook { namespace facebook {
namespace react { namespace react {
/* /*
* Calculates set of mutation instuctions which describe how the old * Calculates a list of view mutations which describes how the old
* ShadowNode tree can be transformed to the new ShadowNode tree. * `ShadowTree` can be transformed to the new one.
* The set of instuctions might be and might not be optimal. * The list of mutations might be and might not be optimal.
*/ */
void calculateMutationInstructions( ShadowViewMutationList calculateShadowViewMutations(
TreeMutationInstructionList &instructions, const ShadowNode &oldRootShadowNode,
const SharedShadowNode &oldNode, const ShadowNode &newRootShadowNode
const SharedShadowNode &newNode
); );
} // namespace react } // namespace react

View File

@ -82,9 +82,9 @@ SchedulerDelegate *Scheduler::getDelegate() const {
#pragma mark - ShadowTreeDelegate #pragma mark - ShadowTreeDelegate
void Scheduler::shadowTreeDidCommit(const SharedShadowTree &shadowTree, const TreeMutationInstructionList &instructions) { void Scheduler::shadowTreeDidCommit(const SharedShadowTree &shadowTree, const ShadowViewMutationList &mutations) {
if (delegate_) { if (delegate_) {
delegate_->schedulerDidComputeMutationInstructions(shadowTree->getRootTag(), instructions); delegate_->schedulerDidFinishTransaction(shadowTree->getRootTag(), mutations);
} }
} }

View File

@ -57,7 +57,7 @@ public:
#pragma mark - ShadowTreeDelegate #pragma mark - ShadowTreeDelegate
void shadowTreeDidCommit(const SharedShadowTree &shadowTree, const TreeMutationInstructionList &instructions) override; void shadowTreeDidCommit(const SharedShadowTree &shadowTree, const ShadowViewMutationList &mutations) override;
#pragma mark - Deprecated #pragma mark - Deprecated
@ -67,7 +67,6 @@ public:
std::shared_ptr<FabricUIManager> getUIManager_DO_NOT_USE(); std::shared_ptr<FabricUIManager> getUIManager_DO_NOT_USE();
private: private:
SchedulerDelegate *delegate_; SchedulerDelegate *delegate_;
std::shared_ptr<FabricUIManager> uiManager_; std::shared_ptr<FabricUIManager> uiManager_;
std::unordered_map<Tag, SharedShadowTree> shadowTreeRegistry_; std::unordered_map<Tag, SharedShadowTree> shadowTreeRegistry_;

View File

@ -9,7 +9,7 @@
#include <fabric/core/ReactPrimitives.h> #include <fabric/core/ReactPrimitives.h>
#include <fabric/core/ShadowNode.h> #include <fabric/core/ShadowNode.h>
#include <fabric/uimanager/TreeMutationInstruction.h> #include <fabric/uimanager/ShadowViewMutation.h>
namespace facebook { namespace facebook {
namespace react { namespace react {
@ -24,10 +24,10 @@ public:
/* /*
* Called right after Scheduler computed (and laid out) a new updated version * Called right after Scheduler computed (and laid out) a new updated version
* of the tree and calculated a set of mutation instructions which are * of the tree and calculated a set of mutations which are suffisient
* suffisient to construct a new one. * to construct a new one.
*/ */
virtual void schedulerDidComputeMutationInstructions(Tag rootTag, const TreeMutationInstructionList &instructions) = 0; virtual void schedulerDidFinishTransaction(Tag rootTag, const ShadowViewMutationList &mutations) = 0;
/* /*
* Called right after a new ShadowNode was created. * Called right after a new ShadowNode was created.

View File

@ -10,7 +10,7 @@
#include "ShadowTreeDelegate.h" #include "ShadowTreeDelegate.h"
#include "Differentiator.h" #include "Differentiator.h"
#include "TreeMutationInstruction.h" #include "ShadowViewMutation.h"
namespace facebook { namespace facebook {
namespace react { namespace react {
@ -78,19 +78,16 @@ void ShadowTree::complete(UnsharedRootShadowNode newRootShadowNode) {
newRootShadowNode->sealRecursive(); newRootShadowNode->sealRecursive();
TreeMutationInstructionList instructions = TreeMutationInstructionList(); auto mutations = calculateShadowViewMutations(
*oldRootShadowNode,
calculateMutationInstructions( *newRootShadowNode
instructions,
oldRootShadowNode,
newRootShadowNode
); );
if (commit(oldRootShadowNode, newRootShadowNode, instructions)) { if (commit(oldRootShadowNode, newRootShadowNode, mutations)) {
emitLayoutEvents(instructions); emitLayoutEvents(mutations);
if (delegate_) { if (delegate_) {
delegate_->shadowTreeDidCommit(shared_from_this(), instructions); delegate_->shadowTreeDidCommit(shared_from_this(), mutations);
} }
} }
} }
@ -98,7 +95,7 @@ void ShadowTree::complete(UnsharedRootShadowNode newRootShadowNode) {
bool ShadowTree::commit( bool ShadowTree::commit(
const SharedRootShadowNode &oldRootShadowNode, const SharedRootShadowNode &oldRootShadowNode,
const SharedRootShadowNode &newRootShadowNode, const SharedRootShadowNode &newRootShadowNode,
const TreeMutationInstructionList &mutationInstructions const ShadowViewMutationList &mutations
) { ) {
std::lock_guard<std::mutex> lock(commitMutex_); std::lock_guard<std::mutex> lock(commitMutex_);
@ -108,69 +105,57 @@ bool ShadowTree::commit(
rootShadowNode_ = newRootShadowNode; rootShadowNode_ = newRootShadowNode;
toggleEventEmitters(mutationInstructions); toggleEventEmitters(mutations);
return true; return true;
} }
void ShadowTree::emitLayoutEvents(const TreeMutationInstructionList &instructions) { void ShadowTree::emitLayoutEvents(const ShadowViewMutationList &mutations) {
for (const auto &instruction : instructions) { for (const auto &mutation : mutations) {
const auto &type = instruction.getType(); // Only `Insert` and `Update` mutations can affect layout metrics.
// Only `Insertion` and `Replacement` instructions can affect layout metrics.
if ( if (
type == TreeMutationInstruction::Insertion || mutation.type != ShadowViewMutation::Insert &&
type == TreeMutationInstruction::Replacement mutation.type != ShadowViewMutation::Update
) { ) {
const auto &newShadowNode = instruction.getNewChildNode(); continue;
const auto &eventEmitter = newShadowNode->getEventEmitter(); }
const auto &viewEventEmitter = std::dynamic_pointer_cast<const ViewEventEmitter>(eventEmitter);
const auto viewEventEmitter = std::dynamic_pointer_cast<const ViewEventEmitter>(mutation.newChildShadowView.eventEmitter);
// Checking if particular shadow node supports `onLayout` event (part of `ViewEventEmitter`). // Checking if particular shadow node supports `onLayout` event (part of `ViewEventEmitter`).
if (viewEventEmitter) { if (!viewEventEmitter) {
// Now we know that both (old and new) shadow nodes must be `LayoutableShadowNode` subclasses. continue;
assert(std::dynamic_pointer_cast<const LayoutableShadowNode>(newShadowNode)); }
// Checking if the `onLayout` event was requested for the particular Shadow Node. // Checking if the `onLayout` event was requested for the particular Shadow Node.
const auto &viewProps = std::dynamic_pointer_cast<const ViewProps>(newShadowNode->getProps()); const auto viewProps = std::dynamic_pointer_cast<const ViewProps>(mutation.newChildShadowView.props);
if (viewProps && !viewProps->onLayout) { if (viewProps && !viewProps->onLayout) {
continue; continue;
} }
// TODO(T29661055): Consider using `std::reinterpret_pointer_cast`. // In case if we have `oldChildShadowView`, checking that layout metrics have changed.
const auto &newLayoutableShadowNode = if (
std::dynamic_pointer_cast<const LayoutableShadowNode>(newShadowNode); mutation.type != ShadowViewMutation::Update &&
mutation.oldChildShadowView.layoutMetrics == mutation.newChildShadowView.layoutMetrics
// In case if we have `oldShadowNode`, we have to check that layout metrics have changed. ) {
if (type == TreeMutationInstruction::Replacement) {
const auto &oldShadowNode = instruction.getOldChildNode();
assert(std::dynamic_pointer_cast<const LayoutableShadowNode>(oldShadowNode));
// TODO(T29661055): Consider using `std::reinterpret_pointer_cast`.
const auto &oldLayoutableShadowNode =
std::dynamic_pointer_cast<const LayoutableShadowNode>(oldShadowNode);
if (oldLayoutableShadowNode->getLayoutMetrics() == newLayoutableShadowNode->getLayoutMetrics()) {
continue; continue;
} }
}
viewEventEmitter->onLayout(newLayoutableShadowNode->getLayoutMetrics()); viewEventEmitter->onLayout(mutation.newChildShadowView.layoutMetrics);
}
}
} }
} }
void ShadowTree::toggleEventEmitters(const TreeMutationInstructionList &instructions) { void ShadowTree::toggleEventEmitters(const ShadowViewMutationList &mutations) {
std::lock_guard<std::recursive_mutex> lock(EventEmitter::DispatchMutex()); std::lock_guard<std::recursive_mutex> lock(EventEmitter::DispatchMutex());
for (const auto &instruction : instructions) { for (const auto &mutation : mutations) {
if (instruction.getType() == TreeMutationInstruction::Deletion) { if (mutation.type == ShadowViewMutation::Delete) {
instruction.getOldChildNode()->getEventEmitter()->setEnabled(false); mutation.oldChildShadowView.eventEmitter->setEnabled(false);
} }
} }
for (const auto &instruction : instructions) { for (const auto &mutation : mutations) {
if (instruction.getType() == TreeMutationInstruction::Creation) { if (mutation.type == ShadowViewMutation::Create) {
instruction.getNewChildNode()->getEventEmitter()->setEnabled(true); mutation.newChildShadowView.eventEmitter->setEnabled(true);
} }
} }
} }

View File

@ -13,6 +13,7 @@
#include <fabric/core/ReactPrimitives.h> #include <fabric/core/ReactPrimitives.h>
#include <fabric/core/ShadowNode.h> #include <fabric/core/ShadowNode.h>
#include <fabric/uimanager/ShadowTreeDelegate.h> #include <fabric/uimanager/ShadowTreeDelegate.h>
#include <fabric/uimanager/ShadowViewMutation.h>
namespace facebook { namespace facebook {
namespace react { namespace react {
@ -80,10 +81,10 @@ private:
bool commit( bool commit(
const SharedRootShadowNode &oldRootShadowNode, const SharedRootShadowNode &oldRootShadowNode,
const SharedRootShadowNode &newRootShadowNode, const SharedRootShadowNode &newRootShadowNode,
const TreeMutationInstructionList &mutationInstructions const ShadowViewMutationList &mutations
); );
void toggleEventEmitters(const TreeMutationInstructionList &instructions); void toggleEventEmitters(const ShadowViewMutationList &mutations);
void emitLayoutEvents(const TreeMutationInstructionList &instructions); void emitLayoutEvents(const ShadowViewMutationList &mutations);
const Tag rootTag_; const Tag rootTag_;
SharedRootShadowNode rootShadowNode_; SharedRootShadowNode rootShadowNode_;

View File

@ -5,7 +5,7 @@
#pragma once #pragma once
#include <fabric/uimanager/TreeMutationInstruction.h> #include <fabric/uimanager/ShadowViewMutation.h>
namespace facebook { namespace facebook {
namespace react { namespace react {
@ -21,7 +21,7 @@ public:
/* /*
* Called right after Shadow Tree commit a new state of the the tree. * Called right after Shadow Tree commit a new state of the the tree.
*/ */
virtual void shadowTreeDidCommit(const std::shared_ptr<ShadowTree> &shadowTree, const TreeMutationInstructionList &instructions) = 0; virtual void shadowTreeDidCommit(const std::shared_ptr<ShadowTree> &shadowTree, const ShadowViewMutationList &mutations) = 0;
}; };
} // namespace react } // namespace react

View File

@ -15,7 +15,7 @@ namespace facebook {
namespace react { namespace react {
/* /*
* Describes a single native view tree mutation instruction which may contain * Describes a single native view tree mutation which may contain
* pointers to an old shadow view, a new shadow view, a parent shadow view and * pointers to an old shadow view, a new shadow view, a parent shadow view and
* final index of inserted or updated view. * final index of inserted or updated view.
* Use static methods to instantiate mutations of different types. * Use static methods to instantiate mutations of different types.
@ -25,21 +25,21 @@ struct ShadowViewMutation final {
#pragma mark - Designated Initializers #pragma mark - Designated Initializers
/* /*
* Creates and returns an `Create` mutation instruction. * Creates and returns an `Create` mutation.
*/ */
static ShadowViewMutation CreateMutation( static ShadowViewMutation CreateMutation(
ShadowView shadowView ShadowView shadowView
); );
/* /*
* Creates and returns an `Delete` mutation instruction. * Creates and returns an `Delete` mutation.
*/ */
static ShadowViewMutation DeleteMutation( static ShadowViewMutation DeleteMutation(
ShadowView shadowView ShadowView shadowView
); );
/* /*
* Creates and returns an `Insert` mutation instruction. * Creates and returns an `Insert` mutation.
*/ */
static ShadowViewMutation InsertMutation( static ShadowViewMutation InsertMutation(
ShadowView parentShadowView, ShadowView parentShadowView,
@ -48,7 +48,7 @@ struct ShadowViewMutation final {
); );
/* /*
* Creates and returns a `Remove` mutation instruction. * Creates and returns a `Remove` mutation.
*/ */
static ShadowViewMutation RemoveMutation( static ShadowViewMutation RemoveMutation(
ShadowView parentShadowView, ShadowView parentShadowView,
@ -57,7 +57,7 @@ struct ShadowViewMutation final {
); );
/* /*
* Creates and returns an `Update` mutation instruction. * Creates and returns an `Update` mutation.
*/ */
static ShadowViewMutation UpdateMutation( static ShadowViewMutation UpdateMutation(
ShadowView parentShadowView, ShadowView parentShadowView,

View File

@ -1,203 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "TreeMutationInstruction.h"
#include <fabric/debug/DebugStringConvertibleItem.h>
namespace facebook {
namespace react {
const TreeMutationInstruction TreeMutationInstruction::Create(
SharedShadowNode node
) {
assert(node);
return TreeMutationInstruction(
Creation,
nullptr,
nullptr,
node,
-1
);
}
const TreeMutationInstruction TreeMutationInstruction::Delete(
SharedShadowNode node
) {
assert(node);
return TreeMutationInstruction(
Deletion,
nullptr,
node,
nullptr,
-1
);
}
const TreeMutationInstruction TreeMutationInstruction::Insert(
SharedShadowNode parentNode,
SharedShadowNode childNode,
int index
) {
assert(parentNode);
assert(childNode);
assert(index != -1);
return TreeMutationInstruction(
Insertion,
parentNode,
nullptr,
childNode,
index
);
}
const TreeMutationInstruction TreeMutationInstruction::Remove(
SharedShadowNode parentNode,
SharedShadowNode childNode,
int index
) {
assert(parentNode);
assert(childNode);
assert(index != -1);
return TreeMutationInstruction(
Removal,
parentNode,
childNode,
nullptr,
index
);
}
const TreeMutationInstruction TreeMutationInstruction::Replace(
SharedShadowNode parentNode,
SharedShadowNode oldChildNode,
SharedShadowNode newChildNode,
int index
) {
assert(oldChildNode);
assert(newChildNode);
return TreeMutationInstruction(
Replacement,
parentNode,
oldChildNode,
newChildNode,
index
);
}
TreeMutationInstruction::TreeMutationInstruction(
Type type,
SharedShadowNode parentNode,
SharedShadowNode oldChildNode,
SharedShadowNode newChildNode,
int index
):
type_(type),
parentNode_(parentNode),
oldChildNode_(oldChildNode),
newChildNode_(newChildNode),
index_(index) {};
#pragma mark - Getters
TreeMutationInstruction::Type TreeMutationInstruction::getType() const {
return type_;
}
SharedShadowNode TreeMutationInstruction::getParentNode() const {
assert(parentNode_);
return parentNode_;
}
SharedShadowNode TreeMutationInstruction::getOldChildNode() const {
assert(oldChildNode_);
return oldChildNode_;
}
SharedShadowNode TreeMutationInstruction::getNewChildNode() const {
assert(newChildNode_);
return newChildNode_;
}
int TreeMutationInstruction::getIndex() const {
assert(index_ != -1);
return index_;
}
#pragma mark - DebugStringConvertible
std::string TreeMutationInstruction::getDebugName() const {
switch (type_) {
case Creation:
return "Create";
case Deletion:
return "Delete";
case Insertion:
return "Insert";
case Removal:
return "Remove";
case Replacement:
return "Replace";
}
};
std::string TreeMutationInstruction::getDebugValue() const {
switch (type_) {
case Creation:
return "[*" + folly::to<std::string>(newChildNode_->getTag()) + "]";
case Deletion:
return "[~" + folly::to<std::string>(oldChildNode_->getTag()) + "]";
case Insertion:
return "[" + folly::to<std::string>(newChildNode_->getTag()) + "->" + folly::to<std::string>(parentNode_->getTag()) + "]";
case Removal:
return "[" + folly::to<std::string>(oldChildNode_->getTag()) + "<~" + folly::to<std::string>(parentNode_->getTag()) + "]";
case Replacement:
return "[=" + folly::to<std::string>(oldChildNode_->getTag()) + "]";
}
};
SharedDebugStringConvertibleList TreeMutationInstruction::getDebugProps() const {
DebugStringConvertibleOptions options = {.maximumDepth = 1, .format = false};
switch (type_) {
case Creation:
return SharedDebugStringConvertibleList {
std::make_shared<DebugStringConvertibleItem>("node", newChildNode_->getDebugDescription(options)),
};
case Deletion:
return SharedDebugStringConvertibleList {
std::make_shared<DebugStringConvertibleItem>("node", oldChildNode_->getDebugDescription(options)),
};
case Insertion:
return SharedDebugStringConvertibleList {
std::make_shared<DebugStringConvertibleItem>("parentNode", parentNode_->getDebugDescription(options)),
std::make_shared<DebugStringConvertibleItem>("childNode", newChildNode_->getDebugDescription(options)),
std::make_shared<DebugStringConvertibleItem>("index", folly::to<std::string>(index_))
};
case Removal:
return SharedDebugStringConvertibleList {
std::make_shared<DebugStringConvertibleItem>("parentNode", parentNode_->getDebugDescription(options)),
std::make_shared<DebugStringConvertibleItem>("childNode", oldChildNode_->getDebugDescription(options)),
std::make_shared<DebugStringConvertibleItem>("index", folly::to<std::string>(index_))
};
case Replacement:
return SharedDebugStringConvertibleList {
std::make_shared<DebugStringConvertibleItem>("parentNode", parentNode_ ? parentNode_->getDebugDescription(options) : "nullptr"),
std::make_shared<DebugStringConvertibleItem>("oldChildNode", oldChildNode_->getDebugDescription(options)),
std::make_shared<DebugStringConvertibleItem>("newChildNode", newChildNode_->getDebugDescription(options)),
std::make_shared<DebugStringConvertibleItem>("index", folly::to<std::string>(index_))
};
}
}
} // namespace react
} // namespace facebook

View File

@ -1,119 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <vector>
#include <fabric/core/ShadowNode.h>
#include <fabric/debug/DebugStringConvertible.h>
namespace facebook {
namespace react {
class TreeMutationInstruction;
using TreeMutationInstructionList = std::vector<TreeMutationInstruction>;
/*
* Describes single native views tree mutation instruction which may contain
* pointers to an old shadow node, a new shadow node, a parent shadow node and
* final index of inserted or updated node.
* The relationship between native view instances and shadow node instances is
* defined by `tag` value.
* Use static methods to instantiate mutation instructions of different types.
*/
class TreeMutationInstruction:
public DebugStringConvertible {
public:
#pragma mark - Designated Initializers
/*
* Creates and returns an *Creation* instruction.
*/
static const TreeMutationInstruction Create(
SharedShadowNode node
);
/*
* Creates and returns an *Deletion* instruction.
*/
static const TreeMutationInstruction Delete(
SharedShadowNode node
);
/*
* Creates and returns an *Insertion* instruction.
*/
static const TreeMutationInstruction Insert(
SharedShadowNode parentNode,
SharedShadowNode childNode,
int index
);
/*
* Creates and returns a *Removal* instruction.
*/
static const TreeMutationInstruction Remove(
SharedShadowNode parentNode,
SharedShadowNode childNode,
int index
);
/*
* Creates and returns an *Replacement* instruction.
*/
static const TreeMutationInstruction Replace(
SharedShadowNode parentNode,
SharedShadowNode oldChildNode,
SharedShadowNode newChildNode,
int index
);
#pragma mark - Type
enum Type {
Creation,
Deletion,
Insertion,
Removal,
Replacement
};
#pragma mark - Getters
Type getType() const;
SharedShadowNode getParentNode() const;
SharedShadowNode getOldChildNode() const;
SharedShadowNode getNewChildNode() const;
int getIndex() const;
#pragma mark - DebugStringConvertible
std::string getDebugName() const override;
std::string getDebugValue() const override;
SharedDebugStringConvertibleList getDebugProps() const override;
private:
TreeMutationInstruction(
Type type,
SharedShadowNode parentNode,
SharedShadowNode oldChildNode,
SharedShadowNode newChildNode,
int index
);
Type type_ {Creation};
SharedShadowNode parentNode_ {nullptr};
SharedShadowNode oldChildNode_ {nullptr};
SharedShadowNode newChildNode_ {nullptr};
int index_ {-1};
};
} // namespace react
} // namespace facebook