'Url' from 'webview' in react native - javascript

I want to know the URL every time it changes in Webview. I searched online a lot. And according to that, I have done these changes. But it is still not working.
What am I doing wrong here. Why its not working.
It only logs for the first time. or it works when I click some other button on the screen.
What am I missing?
import React, { Component } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { WebView } from 'react-native-webview';
const initialUrl = 'https://www.youtube.com/';
let url = '';
class App extends Component {
state = {
url: initialUrl,
};
onNavigationStateChange = navState => {
console.log("navstate=== ",navState)
if (url!==navState.url) {
url = navState.url;
alert(url);
this.setState({
url: url
})
}
};
render() {
const { url } = this.state;
const injectedJs = `
var linkElements = document.getElementsByTagName("a");
linkElements[0].addEventListener("click", function() {
window.postMessage("addAccountSuccess?token=abc1234");
});
linkElements[1].addEventListener("click", function() {
window.postMessage("addAccountError");
`;
return (
<View style={{paddingTop: 24, flex: 1}}>
<Text style={{backgroundColor: 'black', color: 'white'}}>{ url }</Text>
<WebView
style={{ flex: 1}}
source={{
uri: initialUrl,
}}
injectedJavaScript={injectedJs}
startInLoadingState
scalesPageToFit
javaScriptEnabledAndroid={true}
javaScriptEnabled={true}
domStorageEnabled
startInLoadingState={false}
onNavigationStateChange={this.onNavigationStateChange.bind(this)}
onMessage={event => {
alert('MESSAGE >>>>' + event.nativeEvent.data);
}}
onLoadStart={() => {
console.log("LOAD START ");
}}
onLoadEnd={() => {
console.log('LOAD END');
}}
onError={err => {
console.log('ERROR ');
console.log(err);
}}
/>
</View>
);
}
}
export default App;

Related

React Native - External javascript library not working in Webview

I'm trying to use, in my React Native application, the Webview component to simulate the functionality of an HTML web screen. The javascript code snippet below works normally in a web application, the callback passed to PagSeguroDirectPayment.onSenderHashReady receives the senderHash at the appropriate time.
PagSeguroDirectPayment.setSessionId(${sessionId});
PagSeguroDirectPayment.onSenderHashReady(function(response){
var senderHash = response.senderHash;
window.ReactNativeWebView.postMessage("SENDER HASH: " + senderHash);
}, function (error) {
window.alert("Error: " + error);
});
But when that same javascript code is injected into the Webview, the callbaxk is never called. The javascript snippet window.ReactNativeWebView.postMessage is not executed.
Can anyone help me to solve this problem?
Follow the complete code:
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity
} from 'react-native';
import { Navigation } from 'react-native-navigation';
import WebView from 'react-native-webview';
const HTML = `
<html>
<head>
</head>
<body>
<script type="text/javascript" src="https://stc.pagseguro.uol.com.br/pagseguro/api/v2/checkout/pagseguro.directpayment.js"></script>
</body>
</html>
`;
const script = (sessionId) => {
return `
PagSeguroDirectPayment.setSessionId(${sessionId});
PagSeguroDirectPayment.onSenderHashReady(function(response){
var senderHash = response.senderHash;
window.ReactNativeWebView.postMessage("SENDER HASH: " + senderHash);
}, function (error) {
window.alert("Error: " + error);
});
true; // note: this is required, or you'll sometimes get silent failures
`;
}
export default class WebviewTestScreen extends Component {
constructor(props) {
super(props);
this.state = {
sessionId: null
}
}
static options() {
return {
topBar: {
title: {
text: 'Pagamento'
}
}
};
}
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
injectJavaScript() {
PagseguroService.createSession()
.then(sessionId => {
this.setState({sessionId: sessionId});
this.webref.injectJavaScript(script(sessionId));
})
.catch(error => {
console.log('Error', error);
});
}
render() {
return (
<View style={styles.container}>
<WebView
ref={(r) => (this.webref = r)}
source={{html : HTML}}
onMessage={(event) => {
console.log(event.nativeEvent.data);
}}
javaScriptEnabledAndroid={true}
javaScriptEnabled={true}
originWhitelist={['*']}
>
</WebView>
<View>
<TouchableOpacity style={[styles.button]} onPress={this.injectJavaScript.bind(this)}>
<Text style={styles.textButton}>Test</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 0,
backgroundColor: '#fff'
},
button:{
width:"90%",
borderRadius:10,
height: 50,
alignItems:"center",
justifyContent:"center",
margin: 20,
marginTop: 30,
backgroundColor: '#0095ff'
},
textButton:{
color:"white"
}
});

ScrollView in React Native

I created a simple app, that shows a few pictures with titles. It is something like a film gallery, where you can observe all enable films. But when I try to add ScrollView element it doesn't work when I try to scroll on my emulated Android device. How can I fix it? My code looks like this:
import React, { Component } from 'react'
import { View, ScrollView, StyleSheet } from 'react-native'
import { Header, ImageCard } from './src/components/uikit'
const url = 'https://s3.eu-central-1.wasabisys.com/ghashtag/RNForKids/00-Init/data.json'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
title: 'STAR GATE',
data: []
};
}
componentDidMount = async () => {
try {
const response = await fetch(url)
const data = await response.json()
this.setState({ data })
}
catch (e) {
console.log(e)
throw e
}
}
render() {
const { title, data } = this.state
const { container } = style
return (
<View>
<Header title={title} />
<ScrollView>
<View style={container}>
{
data.map(item => {
return <ImageCard data={item} key={item.id} />
})
}
</View>
</ScrollView>
</View>
)
}
}
const style = StyleSheet.create({
container: {
marginTop: 30,
flexDirection: 'row',
flexWrap: 'wrap',
flexShrink: 2,
justifyContent: 'space-around',
marginBottom: 150
}
})
I made it with a guide, so it should work. (Youtubes author hasn't this problem on his video).
The View inside ScrollView looks problematic. Try something like:
<ScrollView contentContainerStyle={container} >
{
data.map(item => {
return <ImageCard data={item} key={item.id} />
})
}
</ScrollView>

Animate static SVG file with react-native-svg

I'm struggling to get react-native-svg animate in React Native. I also tried the method they suggest with react-native-svg-transformation but it didn't suit my case since I'm working with many files and render them dynamically.
Here's my render file:
import React from "react";
import { View, Text } from "react-native";
import { SvgXml } from "react-native-svg";
import SvgAssets from "../resources/SvgAssets";
import AssetsHelper from "../common/AssetsHelper";
class ChineseCharacter extends React.Component {
constructor(props) {
super(props);
this.state = {
xmlData: "",
};
}
render() {
const { xmlData, file } = this.state;
if (xmlData.length === 0) {
return <View />;
}
return (
<View style={{ flex: 1 }}>
<SvgXml xml={xmlData} width="200" height="200" />
</View>
);
}
componentDidMount(): void {
const { character } = this.props;
const characterUnicode = character.charCodeAt(0);
const file = SvgAssets[characterUnicode.toString()];
AssetsHelper(file)
.then(result => {
this.setState({
xmlData: result,
});
})
.catch(err => console.log(err));
}
}
export default ChineseCharacter;
AssetsHelper is basically reading the svg file and convert them to string in order to pass to SvgXml.
SvgAssets is an object with key as the charCode and value is the file, something like this:
const assets = {
"11904": require("./svgs/11904.svg"),
...
}
Thank in advance.
After few struggling hours, I have found a work around for this problem. I don't use react-native-svg anymore, instead I parse the .svg file to string and put it in react-native-webview. Work like a charm!
render() {
// #ts-ignore
const { xmlData, file } = this.state;
if (xmlData.length === 0) {
return <View />;
}
return (
<View style={{ width: 300, height: 300 }}>
<WebView
source={{ html: xmlData }}
style={{ backgroundColor: "transparent", width: 300, height: 300 }}
/>
</View>
);
}
Try to import the svg files inside ChineseCharacter class.
import svgXml11904 from './svgs/11904.svg'
const assets = {
"11904": svgXml11904,
...
}

React native - is there a way to use an object from another page?

I want to know how can I use the "userPrincipalName" on other pages.
what do I need to do to make it work?
in my example, I try to use the "userPrincipalName" object on the MainScreenpage but I don't understand how to do it.
this my example of the Modal page which it has the object "userPrincipalName":
import React,
{ Component } from 'react';
import {
Text,
TouchableOpacity,
StyleSheet,
View,
} from 'react-native';
import Modal from 'react-native-modal';
import PlacesNavigator from '../navigation/PlacesNavigator';
import { LinearGradient } from 'expo-linear-gradient';
import { AzureInstance, AzureLoginView } from 'react-native-azure-ad-2';
const credentials = {
client_id: 'ea00ca9e-8c37-4520-8d80-2c2bb9239bf8',
scope: 'User.Read',
};
export default class Example extends Component {
constructor(props) {
super(props);
this.state = {
visibleModal: 3,
azureLoginObject: {},
loginSuccess: false
};
this.azureInstance = new AzureInstance(credentials);
this._onLoginSuccess = this._onLoginSuccess.bind(this);
}
_renderButton = () => (
<TouchableOpacity
onPress={() => this.setState({ visibleModal: false })}>
<LinearGradient
colors={['#4c669f', '#3b5998', '#192f6a']}
style={{
height: 80,
width: 180,
borderRadius: 10,
backgroundColor: "#2196F3",
justifyContent: 'center',
alignItems: 'center',
marginTop: 50,
}}>
<Text style={{ color: 'white', fontSize: 20, fontWeight: 'bold' }}>כניסה</Text>
</LinearGradient>
</TouchableOpacity>
);
_onLoginSuccess() {
this.azureInstance.getUserInfo().then(result => {
this.setState({
loginSuccess: true,
azureLoginObject: result,
});
console.log(result);
}).catch(err => {
console.log(err);
})
}
renderWelcomeMsg = (currentTime = new Date()) => {
const currentHour = currentTime.getHours()
const splitAfternoon = 12;
const splitEvening = 17;
if (currentHour >= splitAfternoon && currentHour <= splitEvening) {
return 'צהריים טובים,';
} else if (currentHour >= splitEvening) {
return 'ערב טוב,';
}
return 'בוקר טוב,';
}
render() {
if (!this.state.loginSuccess) {
return (
<AzureLoginView
azureInstance={this.azureInstance}
onSuccess={this._onLoginSuccess}
/>)
}
if (this.state.visibleModal === 3) {
const { givenName } = this.state.azureLoginObject;
const { userPrincipalName } = this.state.azureLoginObject;////THIS IS THE OBJECT I WANT
return (
<View style={styles.container}>
<Modal
isVisible={this.state.visibleModal === 3}
animationInTiming={1000}
animationOutTiming={1000}
backdropTransitionInTiming={4000}
backdropTransitionOutTiming={4000}
animationIn={'flipInY'}
>
<LinearGradient
colors={['#43D4FF', 'white']}
style={{ borderRadius: 10 }}>
<View style={styles.modalContent}>
<Text style={{
fontWeight: "bold",
fontSize: 35,
justifyContent: 'center',
alignItems: 'center',
}}>{this.renderWelcomeMsg()} {givenName}
</Text>
<View style={styles.buttonContainer}>
{this._renderButton()}
</View>
</View>
</LinearGradient>
</Modal>
</View>
);
}
return (
<PlacesNavigator />
);
}
}
And this is the MainScreen page that i want to use the object "userPrincipalName" in the Axios:
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
ActivityIndicator,
Platform,
FlatList,
TouchableOpacity,
TouchableHighlight,
WebView
} from "react-native";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import HeaderButton from "../components/HeaderButton";
import axios from "axios";
import moment from 'moment'
import storeService from '../components/storeService'
export default class MainScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
userPrincipalName: null
};
}
getData = () => {
this.setState({ isLoading: true, data: [] })
axios.get("https://harigotphat1.mekorot.co.il/ConfirmPackaotWS/OrderApprove/OrderApp_Get_Orders_To_Approve/" + userPrincipalName.split('#')[0])
.then(res => {
this.setState({
isLoading: false,
data: res.data
});
console.log(res.data);
});
}
componentDidMount() {
this.props.navigation.setParams({ getData: this.getData });
// now we load the data we stored in the async storage
storeService.loadKey('userPrincipalName').then((res) => {
console.log("THIS IS THE userPrincipalName", res) //res will contain the value given the key, store this value in your state and use it any where in the component
})
this.getData()
// this.postData()
}
renderItems = (item, index) => {
const { merhavid, yaamID, ezorID, shemEzor } = item;
return (
<TouchableHighlight style={{
backgroundColor: '#ffff78'
}}>
<TouchableOpacity
style={{
paddingVertical: 15,
paddingHorizontal: 10,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
borderWidth: 0.8,
borderColor: '#d5d7db',
backgroundColor: index % 2 == 0 ? '#d1f0da' : '#f2f5f3',
}}
onPress={() => this.props.navigation.navigate("Info")}>
<Text style={styles.name}>
{ezorID + "" + " |" + " " + merhavid + " " + yaamID + " " + shemEzor}
</Text>
</TouchableOpacity>
</TouchableHighlight>
);
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 0, paddingTop: 300 }}>
<Text style={{ alignSelf: "center", fontWeight: "bold", fontSize: 20 }}>טוען נתונים...</Text>
<ActivityIndicator size={'large'} color={'#08cbfc'} />
</View>
);
}
return (
<>
<View style={styles.container}>
<FlatList
data={this.state.data}
keyExtractor={(_, index) => String(index)}
renderItem={({ item, index }) => { return this.renderItems(item, index) }}
/>
</View>
<View style={styles.bottomMainContainer}>
<View style={styles.bottomView} >
<Text style={styles.bottomTextStyle}>סה"כ: {this.state.data.length} רשומות</Text>
</View>
</View>
</>
);
}
}
This is what React Context was designed for:
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
This also applies to updating data from nested components e.g.
const UserContext = React.createContext({
user: null,
setUser: () => {}
});
function UserContextProvider {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{props.children}
</UserContext.Provider>
)
}
function App() {
return (
<UserContextProvider>
<MainScreen />
</UserContextProvider>
);
}
class MainScreen extends Component {
static contextType = UserContext;
getData() {
// We should see the user details
console.log(this.context.user);
}
render() {
return (
<div>
<Example />
</div>
)
}
}
class Example extends Component {
static contextType = UserContext
_onLoginSuccess() {
this.azureInstance.getUserInfo().then(result => {
this.setState(...);
// set user in UserContext
this.context.setUser(result);
});
}
}
The best way would be use redux store which helps you create immutable data object which can be update only based on user action https://redux.js.org/basics/store/.
Another simple but not efficient way is to use the react native async storage, Where you store the object and later load it up in the componentDidMount() of your new component.
Another way is to pass these props as ScreenProps in your child component(this is only possible if the screens have a parent child relation)
solution - Sharing the object between components using asnc storage
import AsyncStorage from '#react-native-community/async-storage';
const storeService = {
async saveItem(key, value) {
try {
await AsyncStorage.setItem(key, value);
} catch (error) {
console.log('AsyncStorage Error: ' + error.message);
}
},
loadKey(key) {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(key)
.then(res => {
resolve(res)
})
.catch(err => reject(err));
});
}
};
export default storeService;
Note that these stateless component has 2 methods, One is to save against a key and another is to load.
Now to save a value against a key use
import React,
{ Component } from 'react';
....
export default class Example extends Component {
constructor(props) {
super(props);
....
}
....
render() {
...
if (this.state.visibleModal === 3) {
const { givenName } = this.state.azureLoginObject;
const { userPrincipalName } = this.state.azureLoginObject;
//this is how we will store the value when this component Example loads
storeService.saveItem('userPrincipalName', userPrincipalName)
return (
....
);
}
return (
<PlacesNavigator />
);
}
}
And to load this item again use
import React, { Component } from "react";
...
export default class MainScreen extends Component {
constructor(props) {
super(props);
this.state = {
userPricipalName: null //initialize this state variable
data: []
};
}
getData = () => {
...
var userPrincipalName = this.state.userPrincipalName;
axios.get("https://harigotphat1.mekorot.co.il/ConfirmPackaotWS/OrderApprove/OrderApp_Get_Orders_To_Approve/"+userPrincipalName.split('#')[0])
....
}
componentDidMount() {
// now we load the data we stored in the async storage
storeService.loadKey('userPrincipalName').then((res) => {
this.setState({userPricipalName: res}) //store like this
})
this.getData()
}
...
render() {
....
return (
....
);
}
}
Note that in order to be able to save a whole object in async store you will first need to stringify the object which storing and json parse while reading it again.

onSubmitEditing never fires?

Really simple question, why isn't onSubmitEditing firing when I hit 'Search' on the virtual keyboard?
Currently there are no errors thrown and the console.log in onSearch() never fires.
I'm using the EXPO SDK v.29.
import React from 'react';
import { StyleSheet, Text, View, TextInput, ScrollView, Image } from 'react-native';
import { WebBrowser } from 'expo';
import Icon from 'react-native-vector-icons/Ionicons';
import Styles from 'app/constants/Styles';
import Vars from 'app/constants/Vars';
import Menu from 'app/components/Menu';
import MiniMap from 'app/components/MiniMap';
import NewsList from 'app/components/NewsList';
import {get, post} from 'app/helpers/api';
export default class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: (<Image style={{width: 132, height: 30}} source={require('./../../assets/images/header_image.png')}/>)
};
};
constructor(props) {
super(props);
this.state = {
menu: [],
loadingMenu: true,
searchString: '',
};
}
onMenuPress = (item) => {
let next;
let route = item.page_type.slice(4);
if(route == "PageExternal") {
WebBrowser.openBrowserAsync(item.page.url);
} else {
data = item.page;
if(item.children.length > 0) {
route = 'Menu';
data = item.children;
}
this.props.navigation.navigate(route, {
data: data,
title: item.title
});
}
}
onSearch = (e) => {
console.log('onSearch', e);
//WebBrowser.openBrowserAsync('https://www.1177.se/Halland/Sok/?q=Diabetes&submitted=true');
}
async componentDidMount() {
console.log('Eat my shorrs');
menuitems = await get('content/menu');
this.setState({
menu: menuitems,
loadingMenu: false,
})
//this._getMenu();
}
render() {
return (
<ScrollView style={Styles.whiteBackground}>
<View style={[Styles.blueBackground, Styles.topPadding, Styles.horizontalPadding]}>
<View style={[Styles.searchBox, Styles.bottomMargin]}>
<View style={Styles.searchField}>
<TextInput
style = {Styles.searchInput}
placeholder = "Sök sjukdom/behandling"
onSubmitEditing = {(e) => (this.onSearch(e))}
underlineColorAndroid = "transparent"
returnKeyLabel = "Sök på 1177"
returnKeyType = "search"
/>
<Icon style = {Styles.searchIcon} name = "ios-search" size={18}/>
</View>
<Text style={[Styles.searchLabel]}>Söksvaren kommer från 1177.se</Text>
</View>
<Menu
data={this.state.menu}
loading={this.state.loadingMenu}
style={Styles.topPadding}
onItemPress={this.onMenuPress}
/>
</View>
<Text style={[Styles.h1, Styles.blackText, Styles.horizontalPadding]}>Hitta till oss</Text>
<MiniMap navigation={this.props.navigation}></MiniMap>
<Text style={[Styles.h1, Styles.blackText, Styles.horizontalPadding]}>Nyheter</Text>
<NewsList navigation={this.props.navigation}></NewsList>
</ScrollView>
);
}
}
<TextInput
onSubmitEditing = {(event) => (this.onSearch(event.nativeEvent.text))}
multiline={false}
/>
It does not work when multiline={true} is specified, perhaps your styles has that. See Documentation
You will find your text with event.nativeEvent.text
Try changing
onSubmitEditing = {(e) => (this.onSearch(e))}
to
onSubmitEditing = {this.onSearch}
Then keep
onSubmitEditing = {(e) => this.onSearch(e)}
like this and try by changing the function like below
function onSearch(e) {
console.log('onSearch', e);
//WebBrowser.openBrowserAsync('https://www.1177.se/Halland/Sok/?q=Diabetes&submitted=true');
}
Hope this will work
Check this out
https://snack.expo.io/#raajnadar/submit-text-input
Render method
render() {
return (
<View style={styles.container}>
<TextInput
placeholder="Sök sjukdom/behandling"
onSubmitEditing={this.onSearch}
underlineColorAndroid="transparent"
returnKeyLabel="Sök på 1177"
returnKeyType="search"
style={{ width: '100%', textAlign: 'center' }}
/>
</View>
);
}
On submit function
onSearch() {
console.log('onSearch')
}

Categories

Resources