-
Notifications
You must be signed in to change notification settings - Fork 24.4k
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
Conversation
And re-establish inheritance relationship in requireNativeComponent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, Android does not expose baseModuleName
in viewConfig, right? So, this code will break it, right?
@javache What do you think?
React/Views/RCTComponentData.m
Outdated
return @{ | ||
@"propTypes": propTypes, | ||
@"directEvents": directEvents, | ||
@"bubblingEvents": bubblingEvents, | ||
@"baseModuleName": superClass != [NSObject class] ? [self moduleNameForClass:superClass] : [NSNull null] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Please avoid "negative logic": Changing !=
to ==
will make this code easier to read.
...UIManager.RCTView.NativeProps, | ||
...viewConfig.NativeProps, | ||
}; | ||
let baseModuleName = viewConfig.baseModuleName; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
@shergin Android does not expose a The question is of course if it should still merge with |
It does support but how? Does Android use the same approach which we reject for iOS? |
Yeah, the current/old logic does merge for Android as well. My question is if it should do that though? For Android, using JSONDiff to compare what Whilst, for iOS, if you compare So for Android, |
For example the But I grant that I might have misunderstood the logic here. 🤔 |
In this case, I think we should do this:
@javache We need your opinion and blessing here. :) |
React/Views/RCTComponentData.m
Outdated
@@ -455,4 +442,26 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNu | |||
return nil; | |||
} | |||
|
|||
- (NSString *)moduleNameForClass:(Class)managerClass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method doesn't depend on any state, make it a static C-function.
React/Views/RCTComponentData.m
Outdated
return @{ | ||
@"propTypes": propTypes, | ||
@"directEvents": directEvents, | ||
@"bubblingEvents": bubblingEvents, | ||
@"baseModuleName": superClass != [NSObject class] ? [self moduleNameForClass:superClass] : [NSNull null] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use (id)kCFNull
instead of [NSNull null]
Code looks good to me, thanks for providing those JSON diffs! I don't understand yet why Android behaves differently here, we should probably understand that before we land this. |
@shergin has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
Summary: Opening a new PR for #10946 (see discussion there). This PR builds upon #14775 (iOS ViewManager inheritance) and #14261 (more extensible Android WebView). **Motivation** When `WebView.android.js` and `WebView.ios.js` use `requireNativeComponent`, they are hard-coded to require `RCTWebView`. This means if you want to re-use the same JS-logic, but require a custom native WebView-implementation, you have to duplicate the entire JS-code files. The same is true if you want to pass through any custom events or props, which you want to set on the custom native `WebView`. What I'm trying to solve with this PR is to able to extend native WebView logic, and being able to re-use and extend existing WebView JS-logic. This is done by adding a new `nativeConfig` prop on WebView. I've also moved the extra `requireNativeComponent` config to `WebView.extraNativeComponentConfig` for easier re-use. **Test plan** jacobp100 has been kind enough to help me with docs for this new feature. So that is part of the PR and can be read for some information. I've also created an example app which demonstrates how to use this functionality: https://github.com/cbrevik/webview-native-config-example If you've implemented the native side as in the example repo above, it should be fairly easy to use from JavaScript like this: ```javascript import React, { Component, PropTypes } from 'react'; import { WebView, requireNativeComponent, NativeModules } from 'react-native'; const { CustomWebViewManager } = NativeModules; export default class CustomWebView extends Component { static propTypes = { ...WebView.propTypes, finalUrl: PropTypes.string, onNavigationCompleted: PropTypes.func, }; _onNavigationCompleted = (event) => { const { onNavigationCompleted } = this.props; onNavigationCompleted && onNavigationCompleted(event); } render() { return ( <WebView {...this.props} nativeConfig={{ component: RCTCustomWebView, props: { finalUrl: this.props.finalUrl, onNavigationCompleted: this._onNavigationCompleted, }, viewManager: CustomWebViewManager }} /> ); } } const RCTCustomWebView = requireNativeComponent( 'RCTCustomWebView', CustomWebView, WebView.extraNativeComponentConfig ); ``` As you see, you require the custom native implementation at the bottom, and send in that along with any custom props with the `nativeConfig` prop on the `WebView`. You also send in the `viewManager` since iOS requires that for `startLoadWithResult`. **Discussion** As noted in the original PR, this could in principle be done with more React Native components, to make it easier for the community to re-use and extend native components. Closes #15016 Differential Revision: D5701280 Pulled By: hramos fbshipit-source-id: 6c3702654339b037ee81d190c623b8857550e972
Motivation
This is a re-worked version of #14260, by @shergin's suggestion.
For iOS, if you want to inherit from a native ViewManagers, your custom ViewManager will not automatically export the parents' props. So the only way to do this today, is to basically copy/paste the parent ViewManager-file, and add your own custom logic.
With this PR, this is made more extensible by exporting the
baseModuleName
(i.e. the iOSsuperclass
of the ViewManager), and then using that value to re-establish the inheritance relationship inrequireNativeComponent
.Test plan
I've run this with a test project, and it works fine there. But needs more testing.
Opened this PR as per @shergin's suggestion though, so we can discuss approach.
Discussion
UIManager.RCTView.NativeProps
is actually exported by every ViewManager. So shouldUIManager.RCTView.NativeProps
still be merged withviewConfig.NativeProps
, even if the individual ViewManager does not export/use them to begin with?