I'm having trouble getting the Webview on ReactNative to execute the injected JavaScript on a physical Android device. I've scoured the web as far as I could over the past 2 days and still haven't found a solution. Results for testing are as follows:
iOS simulator - All good
iPhone - All good
Android simulator - All good
Physical devices, Sony Xperia Z2, Sony Xperia Z5 Compact and LG G4 - NOTHING
My Webview is defined as follows:
<WebView
style={styles.webView}
source={{
html: html,
baseUrl: 'web/'
}}
injectedJavaScript={'render(' + JSON.stringify(this.state.data) + ');'}
javaScriptEnabledAndroid={true}
scrollEnabled={false}
bounces={false}
renderLoading={() => <LoadingIndicator />}
/>
I've tried specifying javaScriptEnabled as well, to no avail. I also tried smaller scripts to just colour elements on the page or post a message back to the app using window.postMessage, but nothing happens. I need to inject the data to the HTML, which will render graphs for me based on the supplied data. My last resort is to manually construct the HTML with the data appended as part of the markup being supplied to the Webview, but I'd really like to keep it simple and just get it to work the way it should.
I'm using the latest version of ReactNative (0.41) and the phones are running Android 6+.
I just discovered that the Android WebView appears to inject any JS as a single line, even if it includes line breaks. That means that missing semicolons can definitely cause issues, or, in my case, comments delimited by //. Using /* and */ for comments got my injected JavaScript working again.
Well, after leaving this question open for some time and finding a (not so elegant) solution to the problem, I decided I'd share what I ended up doing:
I declared the HTML to be passed to the WebView in a constant string along these lines: const html = '<html>...<script>...[INJECTEDSCRIPT]</script></html>';
I retrieved the data in the componentDidMount() event of the React component's lifecycle and set a state variable for it using this.setState({ data: retrievedData });
This, of course, forced the component to re-render itself, after which I now have the data available to "pass" to the WebView
Seeing as I couldn't find any elegant or usable way of using the injectedJavaScript property to work the way I want it to (as stated in the question), I resorted to replacing the [INJECTEDSCRIPT] value in the HTML constant with the serialized data.
Not the most elegant solution, I know, but it's the only one I could get working reliably across a multitude of devices and emulator configurations. Sample, edited for brevity, as below:
const html = `
<!DOCTYPE html>
<html>
<head>...</head>
<body>...</body>
<script>
var render = function (data) {
...
};
[INJECTEDSCRIPT]
</script>
</html>`;
export class GraphComponent extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount = () => {
SERVICE.getData().done((data) => {
this.setState({ data: data });
});
}
render = () => {
if (!this.state.data)
return <LoadingIndicator />;
let serializedData = JSON.stringify(this.state.data);
return
<WebView
style={styles.webView}
source={{
html: html.replace('[INJECTEDSCRIPT]', 'render(' + serializedData + ');'),
baseUrl: 'web/'
}}
scrollEnabled={false}
bounces={false}
renderLoading={() => <LoadingIndicator />}
/>;
}
}
I have been struggling to get Javascript (pre-embed and injected) to be executed as well. So far, a simple and better solution is to use WebViewBridge. After, everything worked as expected. Here a link to the package: cnpmjs.org/package/react-native-webview-bridge.
Here a demo:
import React, {
Component
} from 'react';
import PropTypes from 'prop-types';
import {
Platform,
WebView,
ActivityIndicator,
} from 'react-native';
import WevViewBridge from 'react-native-webview-bridge';
// TODO: Keep in mind that you should ALWAYS edit
// two separate file and keep them matched.
const IOS_WEB_PAGE_SOURCE = './webPage/wordBody.html';
const ANDROID_WEB_PAGE_SOURCE = 'file:///android_asset/webPage/wordBody.html';
export default class WordBody extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
}
_injectedScript = `
(function(){
let data = ${JSON.stringify(this.props.data)};
refresh(data);
})();
`;
render() {
/*
* We are using Platform.select here for two reasons:
* 0. Everythig work fine for IOS using `WebView` from RN
* 1. Android doesn't load web ressource with `require`
* 2. On Android, `WebView` from react-native doesn't
* execute JavaScript so we use `WebViewBridge`.
*/
return Platform.select({
ios: (<WebView
style = {{backgroundColor: 'rgba(0, 0, 0, 0)', flex:1,}}
scalesPageToFit
source = {require(IOS_WEB_PAGE_SOURCE)}
javaScriptEnabled={true}
originWhitelist={['*']}
injectedJavaScript = {this._injectedScript}
renderLoading={() => <ActivityIndicator/>}
/>),
android: (<WevViewBridge
style = {{backgroundColor: 'rgba(0, 0, 0, 0)', flex:1,}}
scalesPageToFit
source = {{uri:ANDROID_WEB_PAGE_SOURCE}}
javaScriptEnabled={true}
// originWhitelist={['*']}
injectedJavaScript = {this._injectedScript}
renderLoading={() => <ActivityIndicator/>}
/>)
});
};
}
Related
Is there an easy way to open/display a PDF which is linked on a website shown via WebView within react-native-webview within the App or at least within the default PDF app on the phone?
How to "change" behaviour of the website, right now a linked PDF (e.g. Link is opened in the normal browser and than downloaded... instead it should just open up.
I found react-native-pdf and react-native-view-pdf but they seem to simply open local PDFs... How to actually inject and modify links ON the website to have that behaviour?
Thanks
EDIT:
What I tried is:
import Pdf from 'react-native-pdf';
showPdf(pdfurl) {
const source = {uri: pdfurl,cache:true};
//const source = require('./test.pdf'); // ios only
//const source = {uri:'bundle-assets://test.pdf'};
//const source = {uri:'file:///sdcard/test.pdf'};
//const source = {uri:"data:application/pdf;base64,..."};
return (
<View style={styles.container}>
<Pdf
source={source}
onLoadComplete={(numberOfPages,filePath)=>{
console.log(`number of pages: ${numberOfPages}`);
}}
onPageChanged={(page,numberOfPages)=>{
console.log(`current page: ${page}`);
}}
onError={(error)=>{
console.log(error);
}}
style={styles.defaults.pdf} />
</View>
)
}
Within the Webview config:
onShouldStartLoadWithRequest={(event) => {
if (event.url.includes('.pdf')) {
this.showPdf(event.url);
return false;
}
return true;
}}
In my expo / react app I have some HTML from a website that I'm pulling from an api. I'd like to use links within that html to link to pages within the app. Some example html...
responseHtml = "<p>this is a paragraph Some Page<p>"
then I was thinking I would do some string replace so that the html ends up looking like this...
responseHtml = "<p>this is a paragraph Some Page<p>"
Then I would render the content in my app and the links would work if I did something like this...
<HTML onLinkPress={(event, href)=>{Linking.openURL(href)}} html="{responseHtml}" />
I cannot seem to find the right href value to make a successful click to a page within the app. I've tried using the full path, exp://127.0.0.1:19200/Article/some-page, I've tried relative links like /Article/some-page, and I've tried setting a schema value to "myapp" in the app.json and linking to it with myapp://Article/some-page
Have you tried to parse it ? I would use the react-native-htmlview library. There you can customize the Link handler. So you could open the link or pass it down as prop.
Your code could be look like this:
import React from 'react';
import HTMLView from 'react-native-htmlview';
class App extends React.Component {
render() {
const responseHTML = ``;
return (
<HTMLView
value={htmlContent}
stylesheet={{
fontWeight: '300',
color: '#FF3366', // make links coloured pink
}}
/>
);
}
}
If you want to do any custom actions with your link it could look like this:
return (
<HTMLView
value={this.props.putyourhtmlhere}
onLinkPress={(url) => console.log('clicked link: ', url)}
/>
);
Hope that helps
I have integrated fusion chart to my application in React Native. For iOS, Its working fine, But, In android while I tried to display graph, Its showing html content in the UI.
I have followed installation steps properly from github page.
//header declaration
const fusionChartiOSPath = require('../assets/fusioncharts.html');
const fusionChartAndroidPath = { uri: 'file:///android_asset/fusioncharts.html' };
//render method
<FusionCharts
type={type}
width={width}
height={height}
dataFormat={dataFormat}
dataSource={dataSource}
containerBackgroundColor={containerBackgroundColor}
libraryPath={
Platform.OS === 'ios'
? fusionChartiOSPath
: fusionChartAndroidPath
}
/>
But, In UI its showing like
<!DOCTYPE html>
<html>
....
Can anyone suggest me, Where I am doing wrong?
It's happening due to some file issue. Fixed
I have a fairly straight forward set up with react-navigation that utilizes a switch navigator which accepts a param signedIn to set the initial route which looks like this:
const uriPrefix =
Platform.OS === 'android'
? 'myApp://myApp/'
: 'myApp://'
const createRootNavigator = (signedIn = false) => (
createSwitchNavigator({
LoginNav,
AllNav: { screen: AllNav, uriPrefix, path: '' }
}, {
initialRouteName: signedIn ? 'AllNav' : 'LoginNav',
})
);
I then call this in RootContainer:
const Nav = Navigation(isLoggedIn);
return (
<Nav />
)
On iOS this works fine with the uriPrefix and I am able to link to the screens using Linking.openURL() with the link.
On Android it does not work. I am able to make it work on Android without using the switchNavigator and just exporting LoginNav and AllNav, but I still need a switchNavigator.
I followed the instructions here: https://reactnavigation.org/docs/en/deep-linking.html, but am unable to pass uriPrefix in the way they define. I found that at least for iOS, I was able to define that inside of my switchNavigator, but it just does not work the same for me on Android.
Looks like it's a bug in react-navigation:
https://github.com/react-navigation/react-navigation/issues/5027
https://github.com/react-navigation/react-navigation/issues/4913
There's some workaround there that might help, I ended up changing all my navigation into stack navigation to support deep linking =(
I have a mobile site and I want to make an android browser app where I want to open my site.
I have tried and react-native-browser. Something like..
import {
processColor, // make sure to add processColor to your imports if you want to use hex colors as shown below
} from 'react-native';
// at the top of your file near the other imports
var Browser = require('react-native-browser');
class SampleApp extends Component {
render() {
return (
<View style={{paddingTop:20, flex:1}}>
{Browser.open('https://google.com/')}
</View>
);
}
}
But got no success...
I just want to make a browser that opens my mobile site..
Is there any better way of doing this or if someone has any idea how to use react-native-browser ?
Thanks in advance
Looking at the source code, it seems this browser is only available on iOS.
you must search in this web https://js.coach/react-native?search=browser for example https://github.com/d-a-n/react-native-webbrowser
Why are you trying to look for an external library while there is a WebView Component already integrated with react-native itself?
WebView renders web content in a native view. You can use this
component to navigate back and forth in the web view's history and
configure various properties for the web content.
You can just add a WebView and open up the desired web url.
Example
import React, { Component } from 'react';
import { WebView } from 'react-native';
class MyWeb extends Component {
render() {
return (
<WebView
source={{uri: 'https://github.com/facebook/react-native'}}
style={{marginTop: 20}}
/>
);
}
}
You can use https://www.npmjs.com/package/react-native-webbrowser
Install:
npm i react-native-webbrowser --save
Use:
class SampleApp extends Component {
render() {
return (
<View style={{paddingTop:20, flex:1}}>
<Webbrowser
url="https://your-url.com"
hideHomeButton={true}
hideToolbar={true}
hideAddressBar={true}
hideStatusBar={true}
foregroundColor={'#efefef'}
backgroundColor={'#333'}
/>
</View>
);
}
Set all the hide's props to true to make your app display only the site