mirror of
https://github.com/status-im/react-native.git
synced 2025-02-05 14:13:26 +00:00
[ReactNative] Stop traversing the whole view hierarchy every frame
Summary: @public `RCTUIManager` would traverse the whole view hierarchy every time there was any call from JS to Native to call `reactBridgeDidFinishTransaction` on the views that would respond to it. This is a deprecated method that is only implemented by 3 classes, so for now we keep track of these views as they're created and just iterate through them on updates. Test Plan: > NOTE: I tested this on UIExplorer, since the internally none of the classes are used I tried to keep it simple, so I added the following to the old code: ``` __block NSUInteger count = 0; UIView *rootView = _viewRegistry[rootViewTag]; RCTTraverseViewNodes(rootView, ^(id<RCTViewNodeProtocol> view) { count ++; if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) { [view reactBridgeDidFinishTransaction]; } }); NSLog(@"Views iterated: %zd", count); ``` The output after scrolling 20 sections of the `<ListView> - Paging` example was ``` 2015-06-01 00:47:07.351 UIExplorer[67675:1709506] Views iterated: 1549 ``` *every frame* After the change ``` for (id<RCTViewNodeProtocol> node in _bridgeTransactionListeners) { [node reactBridgeDidFinishTransaction]; } NSLog(@"Views iterated: %zd", _bridgeTransactionListeners.count); ``` ``` 2015-06-01 00:51:23.715 UIExplorer[70355:1716465] Views iterated: 3 ``` No matter how many pages are loaded, the output is always 3.
This commit is contained in:
parent
df58789f22
commit
b03446e27e
@ -197,7 +197,8 @@ static UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimatio
|
|||||||
NSMutableDictionary *_defaultViews; // Main thread only
|
NSMutableDictionary *_defaultViews; // Main thread only
|
||||||
NSDictionary *_viewManagers;
|
NSDictionary *_viewManagers;
|
||||||
NSDictionary *_viewConfigs;
|
NSDictionary *_viewConfigs;
|
||||||
NSUInteger _rootTag;
|
|
||||||
|
NSMutableSet *_bridgeTransactionListeners;
|
||||||
}
|
}
|
||||||
|
|
||||||
@synthesize bridge = _bridge;
|
@synthesize bridge = _bridge;
|
||||||
@ -263,7 +264,8 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||||||
// Internal resources
|
// Internal resources
|
||||||
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
||||||
_rootViewTags = [[NSMutableSet alloc] init];
|
_rootViewTags = [[NSMutableSet alloc] init];
|
||||||
_rootTag = 1;
|
|
||||||
|
_bridgeTransactionListeners = [[NSMutableSet alloc] init];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -287,6 +289,7 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||||||
_rootViewTags = nil;
|
_rootViewTags = nil;
|
||||||
_shadowViewRegistry = nil;
|
_shadowViewRegistry = nil;
|
||||||
_viewRegistry = nil;
|
_viewRegistry = nil;
|
||||||
|
_bridgeTransactionListeners = nil;
|
||||||
_bridge = nil;
|
_bridge = nil;
|
||||||
|
|
||||||
[_pendingUIBlocksLock lock];
|
[_pendingUIBlocksLock lock];
|
||||||
@ -397,6 +400,10 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||||||
[(id<RCTInvalidating>)subview invalidate];
|
[(id<RCTInvalidating>)subview invalidate];
|
||||||
}
|
}
|
||||||
registry[subview.reactTag] = nil;
|
registry[subview.reactTag] = nil;
|
||||||
|
|
||||||
|
if (registry == _viewRegistry) {
|
||||||
|
[_bridgeTransactionListeners removeObject:subview];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,7 +489,6 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform layout (possibly animated)
|
// Perform layout (possibly animated)
|
||||||
NSNumber *rootViewTag = rootShadowView.reactTag;
|
|
||||||
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
RCTResponseSenderBlock callback = self->_layoutAnimation.callback;
|
RCTResponseSenderBlock callback = self->_layoutAnimation.callback;
|
||||||
__block NSInteger completionsCalled = 0;
|
__block NSInteger completionsCalled = 0;
|
||||||
@ -547,17 +553,11 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumerate all active (attached to a parent) views and call
|
* TODO(tadeu): Remove it once and for all
|
||||||
* reactBridgeDidFinishTransaction on them if they implement it.
|
|
||||||
* TODO: this is quite inefficient. If this was handled via the
|
|
||||||
* ViewManager instead, it could be done more efficiently.
|
|
||||||
*/
|
*/
|
||||||
UIView *rootView = _viewRegistry[rootViewTag];
|
for (id<RCTViewNodeProtocol> node in _bridgeTransactionListeners) {
|
||||||
RCTTraverseViewNodes(rootView, ^(id<RCTViewNodeProtocol> view) {
|
[node reactBridgeDidFinishTransaction];
|
||||||
if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) {
|
|
||||||
[view reactBridgeDidFinishTransaction];
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -844,6 +844,10 @@ RCT_EXPORT_METHOD(createView:(NSNumber *)reactTag
|
|||||||
view.layer.allowsGroupOpacity = YES; // required for touch handling
|
view.layer.allowsGroupOpacity = YES; // required for touch handling
|
||||||
}
|
}
|
||||||
RCTSetViewProps(props, view, uiManager->_defaultViews[viewName], manager);
|
RCTSetViewProps(props, view, uiManager->_defaultViews[viewName], manager);
|
||||||
|
|
||||||
|
if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) {
|
||||||
|
[uiManager->_bridgeTransactionListeners addObject:view];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
viewRegistry[reactTag] = view;
|
viewRegistry[reactTag] = view;
|
||||||
}];
|
}];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user