Added addChildren() function as a more-optimal replacement for manageChildren for common use cases.

Reviewed By: jspahrsummers

Differential Revision: D2707930

fb-gh-sync-id: c44219bf9af943cad5b57f370656c1bcac732cd9
This commit is contained in:
Nick Lockwood 2015-12-03 10:33:03 -08:00 committed by facebook-github-bot-9
parent ecdc3429cd
commit 9a47ca1cc9
4 changed files with 95 additions and 32 deletions

View File

@ -104,11 +104,11 @@ ReactNativeBaseComponent.Mixin = {
// no children - let's avoid calling out to the native bridge for a large
// portion of the children.
if (mountImages.length) {
var indexes = cachedIndexArray(mountImages.length);
// TODO: Pool these per platform view class. Reusing the `mountImages`
// array would likely be a jit deopt.
var createdTags = [];
for (var i = 0; i < mountImages.length; i++) {
for (var i = 0, l = mountImages.length; i < l; i++) {
var mountImage = mountImages[i];
var childTag = mountImage.tag;
var childID = mountImage.rootNodeID;
@ -122,8 +122,15 @@ ReactNativeBaseComponent.Mixin = {
);
createdTags[i] = mountImage.tag;
}
UIManager
.manageChildren(containerTag, null, null, createdTags, indexes, null);
// Fast path for iOS
if (UIManager.addChildren) {
UIManager.addChildren(containerTag, createdTags);
return;
}
var indexes = cachedIndexArray(mountImages.length);
UIManager.manageChildren(containerTag, null, null, createdTags, indexes, null);
}
},

View File

@ -53,6 +53,7 @@ var dangerouslyProcessChildrenUpdates = function(childrenUpdates, markupList) {
(updates.addChildTags || (updates.addChildTags = [])).push(tag);
}
}
// Note this enumeration order will be different on V8! Move `byContainerTag`
// to a sparse array as soon as we confirm there are not horrible perf
// penalties.

View File

@ -188,6 +188,16 @@ var ReactNativeMount = {
mountImage.rootNodeID,
mountImage.tag
);
// Fast path for iOS
if (UIManager.addChildren) {
UIManager.addChildren(
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID),
[mountImage.tag]
);
return;
}
var addChildTags = [mountImage.tag];
var addAtIndices = [0];
UIManager.manageChildren(

View File

@ -720,6 +720,33 @@ RCT_EXPORT_METHOD(replaceExistingNonRootView:(nonnull NSNumber *)reactTag
removeAtIndices:removeAtIndices];
}
RCT_EXPORT_METHOD(addChildren:(nonnull NSNumber *)containerTag
reactTags:(NSNumberArray *)reactTags)
{
RCTAddChildren(containerTag, reactTags,
(NSDictionary<NSNumber *, id<RCTComponent>> *)_shadowViewRegistry);
[self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
RCTAddChildren(containerTag, reactTags,
(NSDictionary<NSNumber *, id<RCTComponent>> *)viewRegistry);
}];
}
static void RCTAddChildren(NSNumber *containerTag,
NSArray<NSNumber *> *reactTags,
NSDictionary<NSNumber *, id<RCTComponent>> *registry)
{
id<RCTComponent> container = registry[containerTag];
NSInteger index = [container reactSubviews].count;
for (NSNumber *reactTag in reactTags) {
id<RCTComponent> view = registry[reactTag];
if (view) {
[container insertReactSubview:view atIndex:index++];
}
}
}
RCT_EXPORT_METHOD(manageChildren:(nonnull NSNumber *)containerReactTag
moveFromIndices:(NSNumberArray *)moveFromIndices
moveToIndices:(NSNumberArray *)moveToIndices
@ -754,39 +781,57 @@ RCT_EXPORT_METHOD(manageChildren:(nonnull NSNumber *)containerReactTag
removeAtIndices:(NSArray<NSNumber *> *)removeAtIndices
registry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)registry
{
RCTAssert(moveFromIndices.count == moveToIndices.count,
@"moveFromIndices had size %tu, moveToIndices had size %tu",
moveFromIndices.count, moveToIndices.count);
RCTAssert(addChildReactTags.count == addAtIndices.count,
@"addChildReactTags had size %tu, addAtIndices had size %tu",
addChildReactTags.count, addAtIndices.count);
id<RCTComponent> container = registry[containerReactTag];
RCTAssert(moveFromIndices.count == moveToIndices.count, @"moveFromIndices had size %tu, moveToIndices had size %tu", moveFromIndices.count, moveToIndices.count);
RCTAssert(addChildReactTags.count == addAtIndices.count, @"there should be at least one React child to add");
NSArray<id<RCTComponent>> *views = [container reactSubviews];
// Removes (both permanent and temporary moves) are using "before" indices
NSArray<id<RCTComponent>> *permanentlyRemovedChildren =
[self _childrenToRemoveFromContainer:container atIndices:removeAtIndices];
NSArray<id<RCTComponent>> *temporarilyRemovedChildren =
[self _childrenToRemoveFromContainer:container atIndices:moveFromIndices];
[self _removeChildren:permanentlyRemovedChildren fromContainer:container];
[self _removeChildren:temporarilyRemovedChildren fromContainer:container];
[self _purgeChildren:permanentlyRemovedChildren fromRegistry:registry];
// TODO (#5906496): optimize all these loops - constantly calling array.count is not efficient
// Figure out what to insert - merge temporary inserts and adds
NSMutableDictionary *destinationsToChildrenToAdd = [NSMutableDictionary dictionary];
for (NSInteger index = 0, length = temporarilyRemovedChildren.count; index < length; index++) {
destinationsToChildrenToAdd[moveToIndices[index]] = temporarilyRemovedChildren[index];
// Get indices to insert/remove
NSUInteger purgeCount = removeAtIndices.count;
NSUInteger moveCount = moveFromIndices.count;
NSUInteger insertCount = addAtIndices.count;
NSUInteger removeCount = purgeCount + moveCount;
NSMutableArray<id<RCTComponent>> *toRemove = removeCount ? [NSMutableArray new] : nil;
NSMutableDictionary *toInsert = insertCount ? [NSMutableDictionary new] : nil;
for (NSNumber *index in removeAtIndices) {
id<RCTComponent> view = views[index.integerValue];
[toRemove addObject:view];
RCTTraverseViewNodes(registry[view.reactTag], ^(id<RCTComponent> subview) {
RCTAssert(![subview isReactRootView], @"Root views should not be unregistered");
if ([subview conformsToProtocol:@protocol(RCTInvalidating)]) {
[(id<RCTInvalidating>)subview invalidate];
}
for (NSInteger index = 0, length = addAtIndices.count; index < length; index++) {
id<RCTComponent> view = registry[addChildReactTags[index]];
if (view) {
destinationsToChildrenToAdd[addAtIndices[index]] = view;
[registry removeObjectForKey:subview.reactTag];
[_bridgeTransactionListeners removeObject:subview];
});
}
NSInteger i = 0;
for (NSNumber *index in moveFromIndices) {
id<RCTComponent> view = views[index.integerValue];
[toRemove addObject:view];
toInsert[moveToIndices[i]] = view;
i++;
}
i = 0;
for (NSNumber *reactTag in addChildReactTags) {
toInsert[addAtIndices[i]] = registry[reactTag];
i++;
}
NSArray<NSNumber *> *sortedIndices =
[destinationsToChildrenToAdd.allKeys sortedArrayUsingSelector:@selector(compare:)];
for (NSNumber *reactIndex in sortedIndices) {
[container insertReactSubview:destinationsToChildrenToAdd[reactIndex]
atIndex:reactIndex.integerValue];
// Remove old views
for (id<RCTComponent> view in toRemove) {
[container removeReactSubview:view];
}
// Insert new views in ascending order
for (NSNumber *index in [toInsert.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
[container insertReactSubview:toInsert[index] atIndex:index.integerValue];
}
}