Allow UIManager to load native view managers lazily
Summary: This adds a synchronous method that JS can call to load view managers. Notably, we don't have an exact way to go from a JS name to the native view manager, so this naively adds 'Manager' to the end. After lazily loading the view, it makes sure to cache all its values in native and JS, as further calls from JS will fail. Reviewed By: PeteTheHeat Differential Revision: D10204314 fbshipit-source-id: ebf42a85dcc467f3b4c5d6e18e49e04f9e8aa4f9
This commit is contained in:
parent
bbb6a0754c
commit
751be26015
|
@ -37,6 +37,7 @@ UIManager.takeSnapshot = function() {
|
|||
'Use ReactNative.takeSnapshot instead.',
|
||||
);
|
||||
};
|
||||
const triedLoadingConfig = new Set();
|
||||
UIManager.getViewManagerConfig = function(viewManagerName: string) {
|
||||
if (
|
||||
viewManagerConfigs[viewManagerName] === undefined &&
|
||||
|
@ -51,9 +52,59 @@ UIManager.getViewManagerConfig = function(viewManagerName: string) {
|
|||
}
|
||||
}
|
||||
|
||||
const config = viewManagerConfigs[viewManagerName];
|
||||
if (config) {
|
||||
return config;
|
||||
}
|
||||
|
||||
if (UIManager.lazilyLoadView && !triedLoadingConfig.has(viewManagerName)) {
|
||||
const result = UIManager.lazilyLoadView(viewManagerName);
|
||||
triedLoadingConfig.add(viewManagerName);
|
||||
if (result.viewConfig) {
|
||||
UIManager[viewManagerName] = result.viewConfig;
|
||||
lazifyViewManagerConfig(viewManagerName);
|
||||
}
|
||||
}
|
||||
|
||||
return viewManagerConfigs[viewManagerName];
|
||||
};
|
||||
|
||||
function lazifyViewManagerConfig(viewName) {
|
||||
const viewConfig = UIManager[viewName];
|
||||
if (viewConfig.Manager) {
|
||||
viewManagerConfigs[viewName] = viewConfig;
|
||||
defineLazyObjectProperty(viewConfig, 'Constants', {
|
||||
get: () => {
|
||||
const viewManager = NativeModules[viewConfig.Manager];
|
||||
const constants = {};
|
||||
viewManager &&
|
||||
Object.keys(viewManager).forEach(key => {
|
||||
const value = viewManager[key];
|
||||
if (typeof value !== 'function') {
|
||||
constants[key] = value;
|
||||
}
|
||||
});
|
||||
return constants;
|
||||
},
|
||||
});
|
||||
defineLazyObjectProperty(viewConfig, 'Commands', {
|
||||
get: () => {
|
||||
const viewManager = NativeModules[viewConfig.Manager];
|
||||
const commands = {};
|
||||
let index = 0;
|
||||
viewManager &&
|
||||
Object.keys(viewManager).forEach(key => {
|
||||
const value = viewManager[key];
|
||||
if (typeof value === 'function') {
|
||||
commands[key] = index++;
|
||||
}
|
||||
});
|
||||
return commands;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the ViewManager constants and commands into UIManager. This is
|
||||
* only needed for iOS, which puts the constants in the ViewManager
|
||||
|
@ -61,39 +112,7 @@ UIManager.getViewManagerConfig = function(viewManagerName: string) {
|
|||
*/
|
||||
if (Platform.OS === 'ios') {
|
||||
Object.keys(UIManager).forEach(viewName => {
|
||||
const viewConfig = UIManager[viewName];
|
||||
if (viewConfig.Manager) {
|
||||
viewManagerConfigs[viewName] = viewConfig;
|
||||
defineLazyObjectProperty(viewConfig, 'Constants', {
|
||||
get: () => {
|
||||
const viewManager = NativeModules[viewConfig.Manager];
|
||||
const constants = {};
|
||||
viewManager &&
|
||||
Object.keys(viewManager).forEach(key => {
|
||||
const value = viewManager[key];
|
||||
if (typeof value !== 'function') {
|
||||
constants[key] = value;
|
||||
}
|
||||
});
|
||||
return constants;
|
||||
},
|
||||
});
|
||||
defineLazyObjectProperty(viewConfig, 'Commands', {
|
||||
get: () => {
|
||||
const viewManager = NativeModules[viewConfig.Manager];
|
||||
const commands = {};
|
||||
let index = 0;
|
||||
viewManager &&
|
||||
Object.keys(viewManager).forEach(key => {
|
||||
const value = viewManager[key];
|
||||
if (typeof value === 'function') {
|
||||
commands[key] = index++;
|
||||
}
|
||||
});
|
||||
return commands;
|
||||
},
|
||||
});
|
||||
}
|
||||
lazifyViewManagerConfig(viewName);
|
||||
});
|
||||
} else if (UIManager.ViewManagerNames) {
|
||||
// We want to add all the view managers to the UIManager.
|
||||
|
|
|
@ -64,4 +64,5 @@ module.exports = [
|
|||
'focus',
|
||||
'genericBubblingEventTypes',
|
||||
'genericDirectEventTypes',
|
||||
'lazilyLoadView',
|
||||
];
|
||||
|
|
|
@ -68,7 +68,7 @@ NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotif
|
|||
NSHashTable<RCTShadowView *> *_shadowViewsWithUpdatedChildren; // UIManager queue only.
|
||||
|
||||
// Keyed by viewName
|
||||
NSDictionary *_componentDataByName;
|
||||
NSMutableDictionary *_componentDataByName;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
@ -148,18 +148,16 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
_observerCoordinator = [RCTUIManagerObserverCoordinator new];
|
||||
|
||||
// Get view managers from bridge
|
||||
NSMutableDictionary *componentDataByName = [NSMutableDictionary new];
|
||||
// Get view managers from bridge=
|
||||
_componentDataByName = [NSMutableDictionary new];
|
||||
for (Class moduleClass in _bridge.moduleClasses) {
|
||||
if ([moduleClass isSubclassOfClass:[RCTViewManager class]]) {
|
||||
RCTComponentData *componentData = [[RCTComponentData alloc] initWithManagerClass:moduleClass
|
||||
bridge:_bridge];
|
||||
componentDataByName[componentData.name] = componentData;
|
||||
_componentDataByName[componentData.name] = componentData;
|
||||
}
|
||||
}
|
||||
|
||||
_componentDataByName = [componentDataByName copy];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didReceiveNewContentSizeMultiplier)
|
||||
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
|
||||
|
@ -1485,6 +1483,62 @@ RCT_EXPORT_METHOD(clearJSResponder)
|
|||
}];
|
||||
}
|
||||
|
||||
static NSMutableDictionary<NSString *, id> *moduleConstantsForComponent(
|
||||
NSMutableDictionary<NSString *, NSDictionary *> *directEvents,
|
||||
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents,
|
||||
RCTComponentData *componentData) {
|
||||
NSMutableDictionary<NSString *, id> *moduleConstants = [NSMutableDictionary new];
|
||||
|
||||
// Register which event-types this view dispatches.
|
||||
// React needs this for the event plugin.
|
||||
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEventTypes = [NSMutableDictionary new];
|
||||
NSMutableDictionary<NSString *, NSDictionary *> *directEventTypes = [NSMutableDictionary new];
|
||||
|
||||
// Add manager class
|
||||
moduleConstants[@"Manager"] = RCTBridgeModuleNameForClass(componentData.managerClass);
|
||||
|
||||
// Add native props
|
||||
NSDictionary<NSString *, id> *viewConfig = [componentData viewConfig];
|
||||
moduleConstants[@"NativeProps"] = viewConfig[@"propTypes"];
|
||||
moduleConstants[@"baseModuleName"] = viewConfig[@"baseModuleName"];
|
||||
moduleConstants[@"bubblingEventTypes"] = bubblingEventTypes;
|
||||
moduleConstants[@"directEventTypes"] = directEventTypes;
|
||||
|
||||
// Add direct events
|
||||
for (NSString *eventName in viewConfig[@"directEvents"]) {
|
||||
if (!directEvents[eventName]) {
|
||||
directEvents[eventName] = @{
|
||||
@"registrationName": [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"],
|
||||
};
|
||||
}
|
||||
directEventTypes[eventName] = directEvents[eventName];
|
||||
if (RCT_DEBUG && bubblingEvents[eventName]) {
|
||||
RCTLogError(@"Component '%@' re-registered bubbling event '%@' as a "
|
||||
"direct event", componentData.name, eventName);
|
||||
}
|
||||
}
|
||||
|
||||
// Add bubbling events
|
||||
for (NSString *eventName in viewConfig[@"bubblingEvents"]) {
|
||||
if (!bubblingEvents[eventName]) {
|
||||
NSString *bubbleName = [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"];
|
||||
bubblingEvents[eventName] = @{
|
||||
@"phasedRegistrationNames": @{
|
||||
@"bubbled": bubbleName,
|
||||
@"captured": [bubbleName stringByAppendingString:@"Capture"],
|
||||
}
|
||||
};
|
||||
}
|
||||
bubblingEventTypes[eventName] = bubblingEvents[eventName];
|
||||
if (RCT_DEBUG && directEvents[eventName]) {
|
||||
RCTLogError(@"Component '%@' re-registered direct event '%@' as a "
|
||||
"bubbling event", componentData.name, eventName);
|
||||
}
|
||||
}
|
||||
|
||||
return moduleConstants;
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, id> *)constantsToExport
|
||||
{
|
||||
NSMutableDictionary<NSString *, NSDictionary *> *constants = [NSMutableDictionary new];
|
||||
|
@ -1492,62 +1546,43 @@ RCT_EXPORT_METHOD(clearJSResponder)
|
|||
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEvents = [NSMutableDictionary new];
|
||||
|
||||
[_componentDataByName enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) {
|
||||
NSMutableDictionary<NSString *, id> *moduleConstants = [NSMutableDictionary new];
|
||||
|
||||
// Register which event-types this view dispatches.
|
||||
// React needs this for the event plugin.
|
||||
NSMutableDictionary<NSString *, NSDictionary *> *bubblingEventTypes = [NSMutableDictionary new];
|
||||
NSMutableDictionary<NSString *, NSDictionary *> *directEventTypes = [NSMutableDictionary new];
|
||||
|
||||
// Add manager class
|
||||
moduleConstants[@"Manager"] = RCTBridgeModuleNameForClass(componentData.managerClass);
|
||||
|
||||
// Add native props
|
||||
NSDictionary<NSString *, id> *viewConfig = [componentData viewConfig];
|
||||
moduleConstants[@"NativeProps"] = viewConfig[@"propTypes"];
|
||||
moduleConstants[@"baseModuleName"] = viewConfig[@"baseModuleName"];
|
||||
moduleConstants[@"bubblingEventTypes"] = bubblingEventTypes;
|
||||
moduleConstants[@"directEventTypes"] = directEventTypes;
|
||||
|
||||
// Add direct events
|
||||
for (NSString *eventName in viewConfig[@"directEvents"]) {
|
||||
if (!directEvents[eventName]) {
|
||||
directEvents[eventName] = @{
|
||||
@"registrationName": [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"],
|
||||
};
|
||||
}
|
||||
directEventTypes[eventName] = directEvents[eventName];
|
||||
if (RCT_DEBUG && bubblingEvents[eventName]) {
|
||||
RCTLogError(@"Component '%@' re-registered bubbling event '%@' as a "
|
||||
"direct event", componentData.name, eventName);
|
||||
}
|
||||
}
|
||||
|
||||
// Add bubbling events
|
||||
for (NSString *eventName in viewConfig[@"bubblingEvents"]) {
|
||||
if (!bubblingEvents[eventName]) {
|
||||
NSString *bubbleName = [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"];
|
||||
bubblingEvents[eventName] = @{
|
||||
@"phasedRegistrationNames": @{
|
||||
@"bubbled": bubbleName,
|
||||
@"captured": [bubbleName stringByAppendingString:@"Capture"],
|
||||
}
|
||||
};
|
||||
}
|
||||
bubblingEventTypes[eventName] = bubblingEvents[eventName];
|
||||
if (RCT_DEBUG && directEvents[eventName]) {
|
||||
RCTLogError(@"Component '%@' re-registered direct event '%@' as a "
|
||||
"bubbling event", componentData.name, eventName);
|
||||
}
|
||||
}
|
||||
|
||||
RCTAssert(!constants[name], @"UIManager already has constants for %@", componentData.name);
|
||||
constants[name] = moduleConstants;
|
||||
RCTAssert(!constants[name], @"UIManager already has constants for %@", componentData.name);
|
||||
NSMutableDictionary<NSString *, id> *moduleConstants = moduleConstantsForComponent(directEvents, bubblingEvents, componentData);
|
||||
constants[name] = moduleConstants;
|
||||
}];
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(lazilyLoadView:(NSString *)name)
|
||||
{
|
||||
if (_componentDataByName[name]) {
|
||||
return @{};
|
||||
}
|
||||
|
||||
id<RCTBridgeDelegate> delegate = self.bridge.delegate;
|
||||
if (![delegate respondsToSelector:@selector(bridge:didNotFindModule:)]) {
|
||||
return @{};
|
||||
}
|
||||
|
||||
NSString *moduleName = [name stringByAppendingString:@"Manager"];
|
||||
BOOL result = [delegate bridge:self.bridge didNotFindModule:moduleName];
|
||||
if (!result) {
|
||||
return @{};
|
||||
}
|
||||
|
||||
id module = [self.bridge moduleForName:moduleName];
|
||||
RCTComponentData *componentData = [[RCTComponentData alloc] initWithManagerClass:[module class] bridge:self.bridge];
|
||||
_componentDataByName[componentData.name] = componentData;
|
||||
NSMutableDictionary *directEvents = [NSMutableDictionary new];
|
||||
NSMutableDictionary *bubblingEvents = [NSMutableDictionary new];
|
||||
NSMutableDictionary<NSString *, id> *moduleConstants = moduleConstantsForComponent(directEvents, bubblingEvents, componentData);
|
||||
return
|
||||
@{
|
||||
@"viewConfig": moduleConstants,
|
||||
};
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(configureNextLayoutAnimation:(NSDictionary *)config
|
||||
withCallback:(RCTResponseSenderBlock)callback
|
||||
errorCallback:(__unused RCTResponseSenderBlock)errorCallback)
|
||||
|
|
Loading…
Reference in New Issue