So, I have written a library, react-native-webview-bridge, for WebView to handle message passing between React-Native and Javascript in WebView. It is based on WebViewJavascriptBridge.
So since there is no way to extend WebView.ios.js, I ended up copying the source code and renamed and modified it, which is not ideal. What I want is a way to extend a React-Native's Core Components.
I'm looking for some suggestions to guide me through this. How would you guys extend the Core Component? Is it actually possible to extend any React's implementation?
Just to be clear, I am talking about the Javascript portion of React-Native's Components not the objective-c part.
Sadly, I have run into this myself. I am finding that a lot of the core components are really not designed to be extensible.
For example: https://github.com/facebook/react-native/issues/1788
No responses from the FB team.
Currently there are a couple of ways to do this, that I know of:
class SuperText extends Text {
render() {
return (
<View>
<Text style={{color: 'pink', textAlign: 'center'}}>*******************</Text>
{super.render()}
<Text style={{color: 'purple', textAlign: 'center'}}>:) :) :) :) :)</Text>
</View>
)
}
}

@jaygarcia - Facebook created this project but a big part of open source is what the entire community does.. If they don't have a use for a particular feature in their apps, it doesn't make much sense to spend engineering time on it when they have so many other things to work on. So if you run into issues like that I think it would be awesome to dig into it and take ownership of it and try to push your ideas/solutions through. I say this as someone who is not an employee in Facebook that invests a huge amount of my time in this project :smile:. tl;dr: this is OSS, let's try not to blame but instead take it on ourselves to do what we think the software needs to be the best it can be.
@brentvatne, I think you're mistaking my perspective.
I am 100% in alignment with what needs to happen and offered to change things. But rather than spending time (hours?) and energy into what could be a rejected PR, I asked the team if the idea of the change would be acceptable for a PR, which could take minutes to respond to.
There are currently 94 pull requests that need to be responded to. :P
To be clear: I'm an advocate for this tech and even an evangelist. For example, I'm on a train right now heading from NYC back home to DC, where I did a talk at a meetup introducing people to React Native. People who just blame don't do things like that. :)
@brentvatne & @jaygarcia, I think I found a very nice way to extend any core components. At the moment I feel like I just invented the wheel for the first time. :dancer:
So lets say you want to add eval method to WebView component. What you have to do is: use category feature in objective-c.
RCTWebView+WebViewExBridge.h
#import "RCTWebView.h"
@interface RCTWebView (WebViewExBridge)
- (void)eval:(NSString *) value;
@end
RCTWebView+WebViewExBridge.m
#import "RCTWebView+WebViewExBridge.h"
@implementation RCTWebView (WebViewExBridge)
- (void)eval:(NSString *) value {
//This is the only way to get access to private variables in RCTWebView
UIWebView* _webView = [self valueForKey:@"_webView"];
//////////////////////////////////////////////////////
[_webView stringByEvaluatingJavaScriptFromString:value];
NSLog(@"Called Eval %@", value);
}
@end
RCTWebViewManager+WebViewExManager.h
#import "RCTWebViewManager.h"
@interface RCTWebViewManager (WebViewExManager)
@end
RCTWebViewManager+WebViewExManager.m
#import "RCTWebViewManager+WebViewExManager.h"
#import "RCTBridge.h"
#import "RCTSparseArray.h"
#import "RCTUIManager.h"
#import "RCTWebView.h"
#import "RCTWebView+WebViewExBridge.h"
@implementation RCTWebViewManager (WebViewExManager)
//NOTE
//DO not include RCT_EXPORT_MODULE() here because RCTWebViewManager already has it and
//we are using category feature in objective-c
RCT_EXPORT_METHOD(eval:(NSNumber *)reactTag
value:(NSString*)value)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
}
[view eval:value];
}];
}
@end
And the last one is our WebViewBrdige component which wraps WebView as a higher order component(Thanks @brentvatne).
WebViewBridge.js
'use strict';
var React = require('react-native');
var {
WebView,
Component,
NativeModules: {
WebViewManager
}
} = React;
var WEB_VIEW_BRIDGE_REF = 'WEBVIEW_BRIDGE';
class WebViewBridge extends Component {
constructor(props) {
super(props);
}
//exposing objective-c eval as evalScript
evalScript(value) {
var ref = this.refs[WEB_VIEW_BRIDGE_REF];
//we need to get the handler of current WebView in order for our
WebViewManager.eval(ref.getWebWiewHandle(), value);
}
render() {
return (
<WebView
ref={WEB_VIEW_BRIDGE_REF}
{...this.props}/>
);
}
}
module.exports = WebViewBridge;
Now what we can do is using the WebViewBridge as follows:
'use strict';
var React = require('react-native');
var WebViewBridge = require('./WebViewBridge.js');
var Sample2 = React.createClass({
componentDidMount: function () {
this.refs.['myWebViewBridge'].evalScript('window.alert("Booya!")');
},
render: function() {
var url = "http://google.com";
return (
<WebViewBridge ref="myWebViewBridge" url={url}/>
);
}
});
Let me know your thoughts.
@alinz , this is a good pattern. @ide @brentvatne , i believe this thread can now be closed.
@alinz do you have an example of how this could translate for Android?
@wilsonpage for android is either not possible the way that it has been written such as inner classes or you have to use some sort of hack and typecasting the object and inject your implementation which I don't recommend. The best solution is go back to WebView implementation for android and decouple all the classes and then we can extend it.
Just wanna give a heads up to everyone who finds this issue and tries to extend by using {super.render()} in the render function:
This will give you an error in production / release. Not in local development. This will result in:
TypeError: Super expression must either be null or a function, not string
Took me a while to find this. The proper way is to pass props through {...this.props} and have react handle the rest.
Most helpful comment
@alinz do you have an example of how this could translate for Android?