Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support native ViewManager inheritance on iOS #14775

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions Libraries/ReactNative/requireNativeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,19 @@ function requireNativeComponent(
viewConfig.propTypes = null;
}

// The ViewConfig doesn't contain any props inherited from the view manager's
// superclass, so we manually merge in the RCTView ones. Other inheritance
// patterns are currenty not supported.
const nativeProps = {
...UIManager.RCTView.NativeProps,
...viewConfig.NativeProps,
};
let baseModuleName = viewConfig.baseModuleName;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does using do { } while (); loop make this code a bit cleaner/easier-to-read?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do { } while (); would make it execute the statement at least once, which we don't want to do if there is no baseModuleName exported. So not sure if that is a good idea.

let nativeProps = { ...viewConfig.NativeProps };
while (baseModuleName) {
const baseModule = UIManager[baseModuleName];
if (!baseModule) {
warning(false, 'Base module "%s" does not exist', baseModuleName);
baseModuleName = null;
} else {
nativeProps = { ...nativeProps, ...baseModule.NativeProps };
baseModuleName = baseModule.baseModuleName;
}
}

for (const key in nativeProps) {
let useAttribute = false;
const attribute = {};
Expand Down
1 change: 1 addition & 0 deletions React/Modules/RCTUIManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,7 @@ static void RCTMeasureLayout(RCTShadowView *view,
// Add native props
NSDictionary<NSString *, id> *viewConfig = [componentData viewConfig];
moduleConstants[@"NativeProps"] = viewConfig[@"propTypes"];
moduleConstants[@"baseModuleName"] = viewConfig[@"baseModuleName"];

// Add direct events
for (NSString *eventName in viewConfig[@"directEvents"]) {
Expand Down
45 changes: 27 additions & 18 deletions React/Views/RCTComponentData.m
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,7 @@ - (instancetype)initWithManagerClass:(Class)managerClass
_viewPropBlocks = [NSMutableDictionary new];
_shadowPropBlocks = [NSMutableDictionary new];

// Hackety hack, this partially re-implements RCTBridgeModuleNameForClass
// We want to get rid of RCT and RK prefixes, but a lot of JS code still references
// view names by prefix. So, while RCTBridgeModuleNameForClass now drops these
// prefixes by default, we'll still keep them around here.
NSString *name = [managerClass moduleName];
if (name.length == 0) {
name = NSStringFromClass(managerClass);
}
if ([name hasPrefix:@"RK"]) {
name = [name stringByReplacingCharactersInRange:(NSRange){0, @"RK".length} withString:@"RCT"];
}
if ([name hasSuffix:@"Manager"]) {
name = [name substringToIndex:name.length - @"Manager".length];
}

RCTAssert(name.length, @"Invalid moduleName '%@'", name);
_name = name;
_name = moduleNameForClass(managerClass);

_implementsUIBlockToAmendWithShadowViewRegistry = NO;
Class cls = _managerClass;
Expand Down Expand Up @@ -439,11 +423,14 @@ - (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowV
}
}
#endif


Class superClass = [_managerClass superclass];

return @{
@"propTypes": propTypes,
@"directEvents": directEvents,
@"bubblingEvents": bubblingEvents,
@"baseModuleName": superClass == [NSObject class] ? [NSNull null] : moduleNameForClass(superClass)
};
}

Expand All @@ -455,4 +442,26 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNu
return nil;
}

static NSString *moduleNameForClass(Class managerClass)
{
// Hackety hack, this partially re-implements RCTBridgeModuleNameForClass
// We want to get rid of RCT and RK prefixes, but a lot of JS code still references
// view names by prefix. So, while RCTBridgeModuleNameForClass now drops these
// prefixes by default, we'll still keep them around here.
NSString *name = [managerClass moduleName];
if (name.length == 0) {
name = NSStringFromClass(managerClass);
}
if ([name hasPrefix:@"RK"]) {
name = [name stringByReplacingCharactersInRange:(NSRange){0, @"RK".length} withString:@"RCT"];
}
if ([name hasSuffix:@"Manager"]) {
name = [name substringToIndex:name.length - @"Manager".length];
}

RCTAssert(name.length, @"Invalid moduleName '%@'", name);

return name;
}

@end