I’ve successfully managed to send a message from React Native (RN) to a WebView.
What I’m struggling with, is getting the message back from the WebView to RN. There’s no errors showing - it’s just that the message never gets through.
Here is the code which I’m using:
React Native Code
<WebView
ref={webview => (this.webview = webview)}
source={{ uri: "http://www.my-web-site"}}
onLoadEnd={() => this.onLoadEnd()}
onMessage={this.onMessage}
cacheEnabled={false}
originWhitelist={['*']}
javaScriptEnabled={true}
/>
onLoadEnd() {
this.webview.postMessage("RN message");
}
onMessage(message) {
console.log("I can’t see this message!");
}
WebView Code
document.addEventListener("message", function (event) {
setTimeout(function(){document.postMessage("WebView message")}, 3000);
}, false);
Please make sure that the data for each receiver is in and use the data that You need.
And always check the prescribed documents to see how to use parameters and grammar before using them.
RN
onLoadEnd() {
this.webview.postMessage("sendmessage");
}
onMessage(event) {
alert(event.nativeEvent.data);
}
WebView Code
document.addEventListener("message", function (event) {
window.postMessage(event.data);
});
React-native version 5.0 or later
window.ReactNativeWebView.postMessage(event.data);
Oh, at last, I finally came across the answer. It was a line of code which I had originally been trying to use to send a message from RN to the WebView. Turns out, it was the code required for sending from the WebView to RN:
WebView Code
document.addEventListener("message", function (event) {
window.ReactNativeWebView.postMessage(event.data);
}, false);
RN Code
<WebView onMessage={event => console.log(event.nativeEvent.data)} />
This works.
React Native
<WebView source={{ ... }}
containerStyle={{ ... }}
onMessage={ e => { console.log(e.nativeEvent.data) } }
/>
WebView
if(window.ReactNativeWebView) {
// send data object to React Native (only string)
window.ReactNativeWebView.postMessage(JSON.stringify(dataObject))
}
If you want to send complete object from webview to react-native android app then you need to stringify your object first
// Reactjs webapp
onClickSendObject = (item) => {
if(window.ReactNativeWebView) {
window.ReactNativeWebView.postMessage(JSON.stringify(item))
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
In react-native app use onMessage (for functional component)
<WebView
ref={webview => (this.webview = webview)}
source={{ uri: "give source url i.e your webapp link"}}
onMessage={m => onMessage(m.nativeEvent.data)} // functional component and for class component use (this.onMessage)
cacheEnabled={false}
originWhitelist={['*']}
javaScriptEnabled={true}
/>
// onMessage function
const onMessage = (m) => {
const messageData = JSON.parse(m);
console.log(messageData)
}
(window["ReactNativeWebView"] || window).postMessage('hello motiur dear','*');
Related
We are facing some very weird issue with opening keyboard on Android device.
It works great on iOS, 90% on Android devices, but on some devices it simply don't work = the keyboard don't show despite the input field has focus.
We tried it with different approaches (with refs, with timeout and without ref either).
All with the same result :/. One of the devices we are trying has API 30, so it even doesn't seem to be an old API problem.
Have anybody facing similar issue? What can cause the issue? Any tips how to debug it?
Here is code:
export const AddNotificationModal: AddNotificationModalT = ({
visible,
category,
onCloseModal,
}) => {
const input = useRef<TextInput>(null);
...
useEffect(() => {
if (visible) {
// 1.
// InteractionManager.runAfterInteractions(() => {
// if (input?.current) {
// input.current.focus();
// }
// });
// 2.
input.current?.focus();
// 3.
// setTimeout(() => input.current?.focus(), 150);
}
}, [visible]);
return (
<Modal
name={AddNotificationModal.name}
visible={visible}>
...
<View>
<TextInput autoFocus={true} ref={input} />
<Button dark onPress={() => onConfirm()}>
{t('shared:buttons.safe')}
</Button>
</View>
</Modal>
);
};
I have below webview component, I wanted the link to be opened in a mobile browser app rather than in inside current webview in my app.
return() {
<WebView source={{ html: `Google`}} />
}
This worked for me.
import { WebView, Linking, NavState } from 'react-native';
const html = `
Google
Twitter
`
class WebViewWrapper extends Component {
private webview;
handleNavigationStateChange = (event: NavState) => {
if (event.url) {
this.webview.stopLoading();
Linking.openURL(event.url);
}
};
render() {
return (
<WebView
originWhitelist={['*']}
ref={ref => {
this.webview = ref;
}}
source={{ html }}
onNavigationStateChange={this.handleNavigationStateChange}
/>
);
}
}
you can do this:
import { Linking } from 'react-native';
on you launch function
Linking.openURL( some_url );
For more details, follow this full example: Just what you want
Calling stopLoading freezes the WebView and links may stop working after pressing them once. Better to use onShouldStartLoadWithRequest (API Reference).
So the code could look something like this:
import { Linking } from 'react-native';
import { WebView } from 'react-native-webview';
const html = `
Google
Twitter
`;
const shouldStartLoadWithRequest = (req) => {
// open the link in native browser
Linking.openURL(req.url);
// returning false prevents WebView to navigate to new URL
return false;
};
const MyComponent = () => (
<WebView
originWhitelist={['*']}
source={{ html }}
onShouldStartLoadWithRequest={shouldStartLoadWithRequest}
/>
);
export default MyComponent;
Note that onShouldStartLoadWithRequest behaves a bit differently on iOS.
Try this one. This will save your time and patience :)
import HTML from 'react-native-render-html';
<HTML
html={this.state.content.description}
// imagesMaxWidth={Dimensions.get('window').width}
onLinkPress={(evt, href) => { Linking.openURL(href) }}
/>
Fully supportive of android, ios and Expo too. And has some cool customisations you can try with.
https://www.npmjs.com/package/react-native-render-html
I have done a sign in to my app using the native fetch API. I now need to load an image, so am loading it in a <WebView>, however the webview is not using the cookies from the fetch API outside of the webview. Is it possible to tell webview to use those cookies?
You can post a message containing your cookies content to your Webview using postMessage().
You need to get the ref of the Webview then post the message to your webview.
let webviewRef = null;
const cookiesContent = 'My cookies bla bla bla'
render() {
return (
<WebView
onLoad={() => webviewRef.postMessage(cookiesContent)}
ref={webview => { webviewRef = webview; }}
source={{ uri: 'https://YourUrl' }}
/>
);
}
Then in your Website you can create the cookies and use it
<script>
document.addEventListener("message", function(data) {
document.cookie=`cookiesName=${data.data}`;
});
</script>
If you aren't the owner of the website you can still try to inject the javascript with injectedJavaScript props of Webview Component.
const JsCode = 'document.addEventListener("message", function(data) {
document.cookie=`cookiesName=${data.data}`;
});';
<WebView
injectedJavaScript={JsCode}
onLoad={() => webviewRef.postMessage(cookiesContent)}
ref={webview => { webviewRef = webview; }}
source={{ uri: 'https://YourUrl' }}
/>
You can use https://github.com/react-native-community/cookies to get cookies and set them back
I don't really clear how to implement onMessage and postMessage, can I get whole web page but only from react native side.
I mean, I will inject this code using injectedJavaScript
var markup = document.documentElement.innerHTML
window.postMessage(markup)
and I will receive the result using onMessage. Is it posible cause so far I can't do that
yes you can do this all you to have to do is use window.postMessage("message") from your web-page that is going to load in WebView and you can see that message in onMessage prop.
Example:
class Test extends React.Component{
constructor(props){
super(props);
this.state:{
html:''
}
}
componentWillMount(){
this.setState({
html : `<html>
<head>
<script>
window.postMessage("Messga from webView")
</script>
</head>
<body><h1>Hello from webView</h1></body>
</html>`
})
}
render(){
return (
<View style={{flex: 1}}>
<WebView
ref={(reff) => {
this.webView = reff;
}}
source={{html: this.state.html}}
style={[styles.flex1, styles.padding5]}
onMessage={(event)=>{
let message = event.nativeEvent.data;
/* event.nativeEvent.data must be string, i.e. window.postMessage
should send only string.
* */
}}
onNavigationStateChange={(evt)=>{}}
onError={(e) => {
console.warn('error occured', e)
}}/>
</View>
)
}
}
I just added a sample html and rendered it in WebView, you can do the same in your page that you are going to load in WebView.
Or another solution is:
You can use injectedJavaScript or injectJavaScript props of WebView as described here.
postMessage is deprecated :: and now you have to use window.ReactNativeWebView.postMessage(data)
const injectedJavascript = `(function() {
window.postMessage = function(data) {
window.ReactNativeWebView.postMessage(data);
};
})()`;
for a full imagination of a component will be:
export const WebViewComponent = (props) => {
const webViewScript = `
setTimeout(function() {
window.ReactNativeWebView.postMessage(/*your pushed data back to onMessage 'event.nativeEvent.data'*/);
}, 2500);
true; // note: this is required, or you'll sometimes get silent failures
`;
return (
<WebView
source={{
uri: `https://example.com`,
}}
automaticallyAdjustContentInsets={false}
scrollEnabled={false}
onMessage={(event) => {
// do something with `event.nativeEvent.data`
}}
javaScriptEnabled={true}
injectedJavaScript={webViewScript}
domStorageEnabled={true}
style={{ width: "100%", height: /*webViewHeight*/ }}
/>
);
};
Actually
i was looking at the documentation since i use the injectJavascript function of the react native´s webview. And in here https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage , it says that for extensions we need to use a "*" as a property .
So it wouldn´t be like this "window.postMessage("Messga from webView")"
Instead it will need to be window.postMessage("Messga from webView","*") to work.
I want to get title of url from webView in react-native.
For example: I can get title of url from webView in iOS
- (void)webViewDidFinishLoad:(UIWebView *)webView{
titleLabel.text = [webView stringByEvaluatingJavaScriptFromString:#"document.title"];
}
I did not find any solutions so far, so Is there anyone have some ideas?
I'm not sure whether this is the cleanest solution but you can inject some JavaScript into WebView to get some data, and then pass it back.
handleMessage(message) {
console.log(message.nativeEvent.data);
}
render() {
return (
<WebView
source={{ uri: "https://wonderloveapp.com" }}
injectedJavaScript="window.postMessage(document.title)"
onMessage={this.handleMessage}
/>
)
}
References:
https://facebook.github.io/react-native/docs/webview.html#injectedjavascript
https://facebook.github.io/react-native/docs/webview.html#onmessage
You can use injectJavaScript or injectedJavaScript props to inject Javasctipt code to the URLs that you load and get title of the page. Take a look at this answer to learn how to get tile with javascript.
Then when you acquire title you can use onMessage prop to pass to react-native side.
There is a NativeEvent that contains a title property.
https://github.com/react-native-webview/react-native-webview/blob/master/docs/Reference.md#onloadstart
<WebView
source={{
uri: route.params.url,
}}
onLoadStart={(event) => {
navigation.setOptions({
headerTitle: event.nativeEvent.title, // get webpage title
});
}}
></WebView>