How to access one control property in another control in ReactNative - react-native

I want to access this.state.sampleString from HelloWorldApp component
class to CustomWebView component class in react native, but in alert
this.props.sampleString showing 'undefined'.
Here is my code:
class CustomWebView extends Component{
constructor(props){
super(props);
this.state = { text: 'http://www.google.com' };
}
render() {
alert(this.props.sampleString);
return (
<WebView
source={{uri:this.state.text}}
style={{marginTop: 50}}
/>
);
}
}
export default class HelloWorldApp extends Component {
constructor(props) {
super(props);
this.state = { sampleString: 'http://www.google.com' };
this.getValue = this.getValue.bind(this);
}
getValue(){
//console.log(this.state.sampleString);
return this.state.sampleString
}
handleClick = () => {
alert(this.state.sampleString);
}
render(){
const {isFocused} = this.state;
const{onFocus,onBlur} = this.props;
return (
<View style={{
flexDirection: 'column',
height: '100%',
paddingTop: 36
}}>
<View style={{
flexDirection: 'row',
height : '5%',
width : '100%',
justifyContent: 'flex-start',
paddingBottom:3,
paddingTop:1,
marginTop : 20
}}>
<TextInput
selectionColor = {BLUE}
ref = "urltext"
underlineColorAndroid={isFocused?BLUE:LIGHT_GRAY}
onFocus = {this.handleFocus}
onBlur ={this.handleBlur}
style={styles.textInput}
onChangeText={(sampleString) => this.setState({sampleString})}
value={this.state.sampleString}
/>
<Button title="Submit"
onPress = {this.handleClick.bind(this)}
color="#9933ff"
accessibilityLabel="TestButton"/>
</View>
<CustomWebView/>
</View>
);
}
}
})
I need to change url in class CustomWebView on the onPress event of Button in
HelloWorldApp class. And that's why i want to access this.props.sampleString in CustomWebView class.

use CustomWebView like this
<CustomWebView
sampleString={this.state.sampleString}
/>

Please do it as following:
<CustomWebView
sampleString={this.state.sampleString}
/>
on CustomWebView, you should use the props - sampleString, not state variable - text.
class CustomWebView extends Component{
constructor(props){
super(props);
this.state = { text: 'http://www.google.com' };
}
render() {
alert(this.props.sampleString);
return (
<WebView
source={{ uri: this.props.sampleString }}
style={{ marginTop: 50 }}
/>
);
}
}

First of all, you are using the wrong method to update the state at onChangeText as,
onChangeText={(sampleString) => this.setState({sampleString})}
when you use above method you can get an error like,
sampleState is not defined
because you have to define which state you want to update with the updated value in setState method. You have to update state as,
onChangeText={(inputText) => this.setState({sampleString: inputText})}
And then you can pass this sampleString in CustomWebView as prop,
<CustomWebView uri = {this.state.sampleString} />
Finally, you can access this prop in CustomWebView as,
<WebView
source={{ uri: this.props.uri }}
style={{ marginTop: 50 }}
/>
And all done :)
Note: - Because you are updating state at onChangeText, and simultaneously passing this state as prop in CustomWebView. CustomWebView can access this state without button click.

you have to take two state variable. fist for textInput and second is for rendering Webview, which you have to pass in CustomWebView component.
Your HelloWorldApp class should be like following:
export default class HelloWorldApp extends Component {
constructor(props) {
super(props);
this.state = {
textInputData: 'http://www.google.com'
sampleString: 'http://www.google.com'
};
}
handleClick(){
//set sampleString when click on button
this.setState({
sampleString: this.state.textInputData
})
}
render(){
const {isFocused} = this.state;
const{onFocus,onBlur} = this.props;
return (
<View style={{
flexDirection: 'column',
height: '100%',
paddingTop: 36
}}>
<View style={{
flexDirection: 'row',
height : '5%',
width : '100%',
justifyContent: 'flex-start',
paddingBottom:3,
paddingTop:1,
marginTop : 20
}}>
<TextInput
selectionColor = {BLUE}
ref = "urltext"
underlineColorAndroid={isFocused?BLUE:LIGHT_GRAY}
onFocus = {this.handleFocus}
onBlur ={this.handleBlur}
style={styles.textInput}
onChangeText={(inputText) => this.setState({textInputData: inputText})}
value={this.state.textInputData}
/>
<Button title="Submit"
onPress = {this.handleClick.bind(this)}
color="#9933ff"
accessibilityLabel="TestButton"/>
</View>
<CustomWebView
sampleString={this.state.sampleString} />
</View>
);
}
} })
and you will get updated sampleString in componentWillReceiveProps() of CustomWebView component
class CustomWebView extends Component{
constructor(props){
super(props);
this.state = {
text: 'http://www.google.com'
};
}
componentWillMount(){
this.setState({ text:this.props.sampleString })
}
componentWillReceiveProps(newProps){
//when will you update url in textinput and do submit. you will get updated
sampleString here.
this.setState({ text:newProps.sampleString })
}
render() {
return (
<WebView
source={{uri:this.state.text}}
style={{marginTop: 50}}
/>
);
}
}

Related

call Child component function from Parent Header Button

I Have two classes Parent class and the Child class and I want to call the Child class function from the Parent class. the problem is that I want to call the child class function from the Parent Header Button. I tried the refs method but didn't get the result. is there any other way to do it?
child class:
class FilterSlider extends React.Component {
constructor(props) {
super(props);
console.log(props);
}
PanelShow = () => {
this._panel.show();
};
render() {
return (
<SlidingUpPanel
ref={(c) => (this._panel = c)}
draggableRange={{ top: height / 1.2, bottom: -10 }}
animatedValue={this._draggedValue}
showBackdrop={false}
>
<View style={styles.panel}>
<View style={styles.panelHeader}>
<Text style={{ color: '#FFF' }}>Bottom Sheet Peek</Text>
</View>
<View style={styles.container}>
<Text>Bottom Sheet Content</Text>
</View>
</View>
</SlidingUpPanel>
);
}
}
export default FilterSlider;
and parent class:
class ReportsListView extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.navigation.setParams({
openSlider : this._openSlider
});
}
_openSlider() {
this.refs.child.PaenlShow();
}
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
title : navigation.state.params.name,
headerLeft : null,
headerStyle : {
backgroundColor : '#4c8572'
},
headerRight : (
<View style={{ paddingLeft: 10, flexDirection: 'row' }}>
<TouchableOpacity onPress={() => params.openSlider()}> //from this icon i want to call the child PanelShow function.
<Icon name="arrow-left" size={30} color="#ffffff" />
</TouchableOpacity>
</View>
),
headerTitleStyle : {
flex : 1,
color : '#fff',
alignItems : 'center',
justifyContent : 'center',
fontWeight : 'bold'
}
};
render()
{
return(
<FilterSlider ref = 'child'/>
)
}
};
You're using ref property incorrectly. To fix this, you can try this:
Parent component
class ReportsListView extends Component {
componentWillMount(){
this.refs.child = null;
}
render() {
return (
<FilterSlider ref={(ref) => {
this.refs.child = ref
}}/>
)
}
}
Now, you can call your function from child component, e.g: this.refs.child.doSomethings().

Change active image react native

I wanna change the active image when it's pressed. So for example I have two zodiacs signs, Capricorn and taurus, and when the user click on Capricorn the image is rendered in color and if the user click on the taurus sign then the Capricorn sign will be in black and white and the taurus sign will be rendered in color. Actually I've only managed to change from black and white to color using states but it will always render the color image, I can't switch it on and off . Here is my code:
class Horoscope extends React.Component {
constructor(props) {
super(props)
this.state = {
belier:false,
balance:false,
cancer:false,
capricorne:false,
gemeaux:false,
lion:false,
poissons:false,
sagittaire:false,
scorpion:false,
taureau:false,
verseau:false,
vierge:false,
}
}
render() {
return (
<View style={styles.main_container}>
<View style = {{height: 150, backgroundColor: '#F5F5F5'}}>
<View style={{flexDirection:'row', justifyContent: 'space-around', marginVertical: 8 }}>
<TouchableOpacity onPress={() => {this.setState({belier: !this.state.belier})}}>
<Image style = {styles.image} source={ this.state.belier === true ? require("../Images/couleurs/icons8-belier-100.png")
: require("../Images/gris/beliergris.png")}/>
</TouchableOpacity>
<TouchableOpacity onPress={()=> {this.setState({taureau: !this.state.taureau})}}>
<Image style = {styles.image} source={this.state.taureau === true ? require("../Images/couleurs/icons8-taureau-96.png")
: require("../Images/gris/taureaugris.png")}/>
</TouchableOpacity>
</View>
</View>
</View>
)}
EDIT: I have also tried with a state clicked but still not know how to change his value to false when the user click on an other image..
You could have an Image mapper,
const Images = {
taureau: {
active: require("../Images/couleurs/icons8-taureau-96.png"),
inactive: require("../Images/gris/taureaugris.png")
},
belier: {
active: require("../Images/couleurs/icons8-belier-100.png"),
inactive: require("../Images/gris/taureaugris.png")
}
};
class Horoscope extends React.Component {
constructor(props) {
super(props)
this.state = {
belier:false,
balance:false,
cancer:false,
capricorne:false,
gemeaux:false,
lion:false,
poissons:false,
sagittaire:false,
scorpion:false,
taureau:false,
verseau:false,
vierge:false,
}
}
onPress = var => {
this.setState(state => ({
[var]: !this.state[var]
}));
}
getImage = var => {
const isActive = this.state[var];
const { active, inactive } = Images[var];
if(isActive) {
return active;
}
return inactive;
}
render() {
<View style={styles.main_container}>
<View style = {{height: 150, backgroundColor: '#F5F5F5'}}>
<View style={{flexDirection:'row', justifyContent: 'space-around', marginVertical: 8 }}>
<TouchableOpacity onPress={() => { this.onPress('belier') }}>
<Image source={() => { this.getImage('belier') }}/>
</TouchableOpacity>
</View>
</View>
</View>
}
}
So I found a way to have what I wanted using map() but I have a problem with the layout now. I have 12 images to render but I can only show 6. And I want to have them 6 on top and 6 just below. Here is the code if someone need it :
And if someone knows why I can't display my 12 images I would appreciate. (will edit if I found it). Thanks
class Horoscope extends React.Component {
constructor(props) {
super(props)
this.state = {
selectedIndex:0,
selectedIndex2:0,
belier:false,
balance:false,
cancer:false,
capricorne:false,
gemeaux:false,
lion:false,
poissons:false,
sagittaire:false,
scorpion:false,
taureau:false,
verseau:false,
vierge:false,
tabList:[
{label:'1', urlActive:require('../Images/couleurs/icons8-belier-100.png'), urlInactive:require('../Images/gris/beliergris.png')},
{label:'2', urlActive:require('../Images/couleurs/icons8-taureau-96.png'), urlInactive:require('../Images/gris/taureaugris.png')},
{label:'3', urlActive:require('../Images/couleurs/icons8-gemeaux-96.png'), urlInactive:require('../Images/gris/gemeauxgris.png')},
{label:'4', urlActive:require('../Images/couleurs/icons8-cancer-96.png'), urlInactive:require('../Images/gris/cancergris.png')},
{label:'5', urlActive:require('../Images/couleurs/icons8-lion-96.png'), urlInactive:require('../Images/gris/liongris.png')},
{label:'6', urlActive:require('../Images/couleurs/icons8-vierge-96.png'), urlInactive:require('../Images/gris/viergegris.png')},
{label:'7', urlActive2:require('../Images/couleurs/icons8-balance-96.png'), urlInactive2:require('../Images/gris/balancegris.png')},
{label:'8', urlActive2:require('../Images/couleurs/icons8-scorpion-96.png'), urlInactive2:require('../Images/gris/scorpiongris.png')},
{label:'9', urlActive2:require('../Images/couleurs/icons8-sagittaire-96.png'), urlInactive2:require('../Images/gris/sagittairegris.png')},
{label:'10', urlActive2:require('../Images/couleurs/icons8-verseau-96.png'), urlInactive2:require('../Images/gris/verseaugris.png')},
{label:'11', urlActive2:require('../Images/couleurs/icons8-capricorne-96.png'), urlInactive2:require('../Images/gris/capricornegris.png')},
{label:'12', urlActive2:require('../Images/couleurs/icons8-poissons-96.png'), urlInactive2:require('../Images/gris/poissonsgris.png')}
]
}
}
render() {
{console.log(this.state.selectedIndex)}
return (
<View style={styles.main_container}>
<View style = {{height: 150, backgroundColor: '#F5F5F5'}}>
<View style={{flexDirection:'row', justifyContent: 'space-between', flexWrap: 'wrap'}}>
{
//loop throught the state
this.state.tabList.map((item,index)=>{
return(
<View>
<TouchableOpacity onPress={()=>{this.setState({selectedIndex:index})}}>
<Image
style = {styles.image}
source={this.state.selectedIndex==index ? item.urlActive:item.urlInactive}/>
</TouchableOpacity>
</View>
)
})
}
</View>
</View>
</View>
)}
}
EDIT: Just using flexWrap: 'wrap'resolve it.

Update an input field in the webview from react-native component

I have a webview component like this:
export default class Home extends React.Component {
onMessage(m) {
//Update an input field in the webview with a custom value
}
render(){
let jsCode = '';
return (
<WebView
ref={(webView) => { this.webView.ref = webView; }}
injectedJavaScript={jsCode}
url={this.state.url}
onMessage={m => this.onMessage(m)}
/>
)
}
}
The webpage has an input field with an id='inpOne'. onMessage prop is triggered by a button click inside the webpage. How to update the input field with the above id when the onMessage prop is executed?
Stripped most of the code for brevity.
Probably like this.
export default class Home extends React.Component {
onMessage(m) {
const js = `document.getElementById('inpOne').value = ${m};`
this.webView.injectJavaScript(js);
}
}
Also, check your WebView's ref prop definition. It looks incorrect. Should be ref={ref => (this.webView = ref)}
Here is the full code of how to change HTML inside of WebWiew from React Native
import React, { Component } from 'react';
import { Text, View, TouchableHighlight } from 'react-native';
import { WebView } from 'react-native-webview';
export default class Sample extends Component {
constructor(props) {
super(props);
}
sendDataFromReactNativeToWebView() {
let injectedData = `document.getElementById("login_field").value = 'xyz#github.com';`;
this.webView.injectJavaScript(injectedData);
}
render() {
return (
<View style={{ flex: 1, marginTop: 30 }}>
<TouchableHighlight style={{ padding: 10, backgroundColor: 'gray', marginTop: 20 }} onPress={() => this.sendDataFromReactNativeToWebView()}>
<Text style={{ color: 'white' }}>Send Data To WebView from React Native</Text>
</TouchableHighlight>
<WebView
style={{ flex: 1 }}
source={{ uri: 'https://github.com/login' }}
ref={(webView) => this.webView = webView}
/>
</View>
);
}
}

Add a button on top of keyboard when called

Im trying to see if I can add a button on top of the keyboard when its called i've seen this feature in many apps but not sure if its possible on expo and react-native. the button should appear with the keyboard and become invisible when the pressed or the keyboard goes away. Ive put a my sample class that goes with the added image
import React, {Component} from 'react';
import {
StyleSheet,
View,
TextInput
} from 'react-native';
export default class test extends Component {
constructor(props) {
super(props);
this.state = {
a: props.navigation,
b: '',
}
}
componentWillMount() {
}
render() {
return (
<View style={styles.container}>
<TextInput
style={{marginTop: 300,marginLeft: 50,borderWidth: 1}}
placeholder="type"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 22,
backgroundColor: '#42444f',
},
});
try this approach
constructor(props)
{
super(props)
this.state = {
buttonVisible: false // add this state. this state is the flag for button appearance
}
}
componentDidMount() {
this.keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
this._keyboardDidShow,
);
this.keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
this._keyboardDidHide,
);
}
_keyboardDidShow = () => {
this.setState({
buttonVisible: true
})
}
_keyboardDidHide = () => {
this.setState({
buttonVisible: false
})
}
render() {
return (
<View style={styles.container}>
<View style={{ flex: 4 }}>
<TextInput
style={{marginTop: 300,marginLeft: 50,borderWidth: 1}}
placeholder="type"
/>
</View>
<View style={{ flex: 1 }}>
{this.state.buttonVisible ? <TouchableOpacity style={{ flex: 1, backgroundColor: 'red', justifyContent: 'center', alignItems: 'center' }}><Text>Button</Text></TouchableOpacity> : <View/>}
</View>
</View>
);
}
the button will be visible depending on the buttonVisible state value, the state changed inside the keyboard event listener that initialized in componentDidMount

React-Native -> Couldn't change style using state

I've got a problem with my app. I'm trying to change style in my child component using states but unfortunately i dont get any results. I tried evrythig i know, but i dont know what is going on and why styles aren't changing at all
Here is parent :
constructor(props) {
super(props);
this.state = {
number: 1,
cords: [
],
choosenCords: [
],
style: {flex:1,flexDirection:'row',backgroundColor:'red'}
};
}
<View style={{ marginTop: 3 }}>
<ListItem style={this.state.style} test={this.state.test} title={item.timestamp} context={item.longtitude + " " + item.latitude} click={this.getData.bind(this, item.longtitude, item.latitude, item.timestamp)} />
</View>
getData = async(x, y, z) => {
let tempTab = []
tempTab.push(x)
tempTab.push(y)
tempTab.push(z)
const changeTest = this.state.test * (-1)
await this.setState({
choosenCords: [...this.state.choosenCords, tempTab],
style: {flex:1,flexDirection:'row',backgroundColor:'blue'}
})
}
}
This fragment of code represent changing style onPress. "choosenCords" are changing, but "style" don't.
Here is child:
class ListItem extends Component {
constructor(props) {
super(props);
this.state = {
test:this.props.test
};
}
render() {
return (
<View style={this.props.style}>
<Image
source={require('../Images/gps.png')}
style={{ borderWidth: 3, borderColor: '#7887AB', borderRadius: 50, marginLeft: 15, marginTop: 10 }}
/>
<View style={{ flex: 1, flexDirection: "column", marginBottom: 15, marginLeft: 10 }}>
<Text style={{ marginLeft: 10, fontSize: 16 }}>
{
"Timestamp: " + this.props.title
}
</Text>
<Text style={{ marginLeft: 15, fontSize: 15 }}>
{
"Cords:" + this.props.context
}
</Text>
</View>
<TouchableHighlight
underlayColor={'transparent'}
onPress={
this.props.click
}>
<Image
source={
require('../Images/check.png')
}
style={{ marginRight: 20 }}
/>
</TouchableHighlight>
</View>
);
}
Could someone help me with that?
You have to do like this:
const customStyle= this.state.test ? styles.custom_1: styles.custom_2
return <View
style={[ styles.anyStyle, customStyle]}
/>;
Define your custom style variable, set it based on state, then use it as an array for your element.
The child component does not automatically update when the state of the parent component changes. You'll need to update the state of the child component manually when the parent sends new props.
//in child component
componentDidUpdate(previousProps, previousState){
if(previousProps.style !== this.props.style){
//update child state here
this.setState({style: this.props.style})
}
}
Now instead of using "style={this.props.style}" in the child, use "style={this.state.style}"
It is happening because when you update your state, child component will not update automatically. you will get new updated value in componentWillReceiveProps() method.
your ListItem class will be like below:
class ListItem extends Component {
constructor(props) {
super(props);
this.state = {
test:this.props.test,
style:'' // take style into your state
};
}
componentWillMount(){
this.setState({style: this.props.style}) //set style
}
componentWillReceiveProps(newProps){
//called when you update style
if(newProps.style !== this.props.style){
this.setState({style: newProps.style})
}
}
render() {
return (
<View style={this.state.style}> //set style for views from state
// add your views here
</View>
);
}
}