No matter what I try I can't seem to get my component to update. Below is my code:
import React, { Component } from "react";
import { StyleSheet, View, TouchableOpacity, Image, Text } from "react-native";
import PropTypes from "prop-types";
import { toggleSound } from "logic/Music.js";
import soundOn from "assets/img/swipe.png";
import soundOff from "assets/img/illum-triangle.png";
export default class GlobalSoundButton extends Component {
constructor(props) {
super(props);
this.state = {
audioOff: this.props.audioOff
};
}
componentDidUpdate = prevProps => {
if (prevProps.audioOff !== this.props.audioOff) {
this.setState({ audioOff: this.props.audioOff }, () => {
console.log("SWITCHING STATE", this.state);
};
this.forceUpdate();
}
};
render() {
return (
<View style={styles.soundButtonWrapper} key={this.props.name}>
<TouchableOpacity
style={styles.soundButtonPress}
activeOpacity={0.8}
onPress={() => {
toggleSound(this.props.sounds);
}}
>
<Image
style={styles.soundButton}
source={this.state.audioOff ? soundOff : soundOn}
></Image>
<Text style={styles.text}>
Audio {this.state.audioOff ? "off" : "on"}
</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
soundButtonWrapper: {
width: "100%",
height: 40,
position: "absolute",
top: 20
},
soundButtonPress: {
width: 40,
height: 40,
position: "absolute",
right: 20
},
soundButton: {
width: 40,
height: 40,
tintColor: "#59BC92"
},
text: {
width: 120,
height: 40,
position: "absolute",
right: 80,
color: "white"
}
});
GlobalSoundButton.propTypes = {
sounds: PropTypes.array
};
The weird part is that I'm using this component in two different screens and in one case it works absolutely fine but in the other screen it doesn't re-render when the audio is changed.
Every time I press it the props get updated so I KNOW that the state updates. The console log always writes out different state so I know the state is updated. I also tried forceUpdate() and adding a key (you can see the key in the code) but it doesn't help.
I'm at a loss here. Has anyone bumped into the same issue or can help? I also know there are several questions quite like this one but no one answered my questions so I had to write my own.
I removed a lot of unrelated code here but basically I pass the props down the following way in my HomeScreen (it's the GlobalSoundButton, and in this case it works):
import GLOBAL from "logic/GlobalState.js";
export default class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
audioOff: false,
};
}
async componentDidMount() {
let audioOff;
try {
audioOff = await retrieveData("audioOff");
} catch (err) {
console.error(err);
}
this.setState(
{
audioOff: audioOff === "true"
});
render() {
GLOBAL.homeScreen = this;
return (
<View style={styles.container}>
<View style={styles.bg}>
<ImageBackground
source={pattern}
style={styles.img}
resizeMode="repeat"
imageStyle={styles.innerImg}
/>
</View>
<View style={styles.container}>
<GlobalSoundButton
sounds={this.state.allSounds}
audioOff={this.state.audioOff}
name="HomeScreenKey"
></GlobalSoundButton>
<Text
style={[styles.smallText, styles.ppText]}
onPress={() =>
Linking.openURL(
"https://privacy-illuminati-flip.netlify.com"
).catch(err => console.error("An error occurred", err))
}
>
Privacy Policy
</Text>
<Text style={styles.welcome}>Illuminati Flip</Text>
<TouchableWithoutFeedback
onPressIn={evt => this.grabFinger(evt)}
onPressOut={evt => this.swipe(evt)}
pressRetentionOffset={{ top: 100, left: 0, bottom: 100, right: 0 }}
>
<View style={styles.swipeArea}>
<View style={styles.gridSelection}>
<Animated.View
pointerEvents={"none"}
style={{
...styles.innerGridSelection,
transform: [{ translateX: this.springValue }]
}}
>
{difficulties}
</Animated.View>
</View>
<View style={styles.margin}>
<Animated.Image
source={swipeFinger}
style={{
...styles.hand,
transform: [
{
translateX: this.fingerValue.interpolate({
inputRange: [-1, 1],
outputRange: [-15, 15]
})
}
]
}}
/>
<Text style={styles.smallText}>Swipe to change difficulty</Text>
</View>
</View>
</TouchableWithoutFeedback>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => {
this.checkTutorial();
}}
>
<Text style={styles.button}>Begin Ceremony</Text>
</TouchableOpacity>
<Text
style={[styles.smallText, styles.musicText]}
onPress={() =>
Linking.openURL("https://fanlink.to/triobgame").catch(err =>
console.error("An error occurred", err)
)
}
>
Music by #triobelisk
</Text>
<Text
style={[styles.smallText, styles.devText]}
onPress={() =>
Linking.openURL("https://twitter.com/iamjohnhult").catch(err =>
console.error("An error occurred", err)
)
}
>
Developed by #iamjohnhult
</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
...
});
The difference in my other component where it doesn't work I render it like this:
import GLOBAL from "logic/GlobalState.js";
<GlobalSoundButton
sounds={this.state.allSounds}
audioOff={GLOBAL.homeScreen.state.audioOff}
name="GameScreenKey"
></GlobalSoundButton>
And the GlobalState.js is as follows:
module.exports = {
homeScreen: null
};
Related
I have started react-native recently and have been working on this app for the past few days and everything was going well. Then I re-wrote this in class(Code snippet bellow) and suddenly I started getting an error undefined is not an object (evaluating 'value.getvalue) "replace <View /> by animated <Animated.View />".even I changed the <View /> to <Animated.View>..still I am getting this error where I mapped the Array. before using class I was using useState() and 'useEffect()' to rendering time.please help to fix it.Thank you
code snippet
import * as React from 'react';
import {Image,StyleSheet,SafeAreaView,ScrollView} from 'react-native';
import {Text,View,Button,TextField,StackAggregator,Colors,TabBar} from 'react-native-ui-lib';
import 'react-native-gesture-handler';
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen';
import Animated from 'react-native-reanimated';
export default class HomeScreen extends React.Component{
constructor({route,navigation}){
super();
this.setTime = [];
this.url = "https://xyz.zxy?latitude=19.2881825&longitude=72.8568436";
this.state ={IsLoading:true};
fetch(this.url)
.then(response => response.json() )
.then(datas => {
var tdata = datas['data'][0];
console.log(tdata);
var timings = tdata['timings'];
this.setTime.push(timings['Mon']);
this.setTime.push(timings['Tue']);
this.setTime.push(timings['Wed']);
this.setTime.push(timings['Thu']);
this.setTime.push(timings['Fri']);
this.setTime.push(timings['Sat']);
this.setTime.push(timings['Sun']);
})
.catch(error => console.log(error))
.finally(() => this.setState({IsLoading:false}));
}
render(){
return (
<SafeAreaView style={homestyles.container}>
<ScrollView bg-dark80 keyboardShouldPersis1tTaps={'handled'} showsVerticalScrollIndicator={false}>
<Animated.View>
<StackAggregator
collapsed={true}
contentContainerStyle={{width:wp('91%'),height:hp('18%'),backgroundColor:"#bd9a3e"}}
itemBorderRadius={10}
>
{
this.setTime.map((Time,key)=>(
<Animated.View>
<Animated.View style={homestyles.timecrd_ctr}>
<Image style={homestyles.bgImage} source={require('./assets/timecard.png')} resizeMode='cover' />
<Text style={homestyles.crdtm_text}>{Time}</Text>
</Animated.View>
</Animated.View>
))
}
</StackAggregator>
</Animated.View>
</ScrollView>
</SafeAreaView>
);
}
}
const homestyles = StyleSheet.create({
timecrd_ctr:{
paddingLeft:wp('2.5%'),
paddingBottom:hp('2.5%'),
paddingTop:hp('2.5%'),
paddingRight:wp('2.5%'),
},
crdtime_card:{
backgroundColor:'black',
},
crdtm_text:{
color:'white',
fontSize: 30,
},
container:{
flex:1,
paddingTop:hp('2.5%'),
backgroundColor:"#001D25",
},
bgImage:{
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
width:wp('92%'),
height:hp('20%'),
transform:[{scaleX: -1}],
},
});
I am pretty new to React Native, so if the question seems stupid i am sorry in advance, i have been trying for hours to get this to work before asking this question.
I am trying to pass data from a text input to the next screen using firebase database.
I keep getting the error JSON Parse error: Unexpected identifier "undefined", I have attached below my two files, one with the text input (InputForm.js) and the other one which i want to show that data on the screen (ASTConfig1screen)
If anyone can help with this you really are a superstar!
InputForm.js
import React, { Component } from 'react';
import { StyleSheet, ScrollView, ActivityIndicator, View, TextInput } from 'react-native';
import { Button } from 'react-native-elements';
import firebase from '../Firebase';
import { withNavigation } from 'react-navigation';
class InputForm extends Component {
constructor() {
super();
this.ref = firebase.firestore().collection('boards');
this.state = {
name: '',
inventoryclass: '',
outlet: '',
isLoading: false,
};
}
updateTextInput = (text, field) => {
const state = this.state
state[field] = text;
this.setState(state);
}
saveBoard() {
this.setState({
isLoading: true,
});
this.ref.add({
name: this.state.name,
inventoryclass: this.state.inventoryclass,
outlet: this.state.outlet,
}).then((docRef) => {
this.setState({
name: '',
outlet: '',
inventoryclass: '',
isLoading: false,
});
this.props.navigation.navigate('ASTConfig1');
})
.catch((error) => {
console.error("Error adding document: ", error);
this.setState({
isLoading: false,
});
});
}
render() {
if(this.state.isLoading){
return(
<View style={styles.activity}>
<ActivityIndicator size="large" color="#ffa500"/>
</View>
)
}
return (
<ScrollView style={styles.container}>
<View style={styles.subContainer}>
<TextInput style={styles.inputtext}
placeholder={'Name'}
placeholderTextColor="white"
value={this.state.name}
onChangeText={(text) => this.updateTextInput(text, 'name')}
/>
</View>
<View style={styles.subContainer}>
<TextInput style={styles.inputtext}
multiline={true}
numberOfLines={4}
placeholder={'Inventory Class'}
placeholderTextColor="white"
value={this.state.inventoryclass}
onChangeText={(text) => this.updateTextInput(text, 'inventoryclass')}
/>
</View>
<View style={styles.subContainer}>
<TextInput style={styles.inputtext}
placeholder={'Outlet'}
placeholderTextColor="white"
value={this.state.outlet}
onChangeText={(text) => this.updateTextInput(text, 'outlet')}
/>
</View>
<View style={styles.button}>
<Button
large
leftIcon={{name: 'save'}}
title='Save'
onPress={() => this.saveBoard()} />
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20
},
subContainer: {
flex: 1,
marginBottom: 20,
padding: 5,
borderBottomWidth: 2,
borderBottomColor: '#CCCCCC',
},
inputtext: {
color: 'white',
fontSize: 20
},
activity: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
}
})
export default withNavigation(InputForm);
and this is where i would like to show the data on this screen
ASTConfig1.js
import React from 'react';
import {
View,
Text,
StyleSheet,
Button,
ImageBackground,
StatusBar,
SafeAreaView,
TextInput,
ScrollView,
UIManager,
ActivityIndicator
}
from 'react-native';
import {AccordionList} from "accordion-collapse-react-native";
import { Separator } from 'native-base';
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen';
import firebase from '../Firebase';
import Logo from '.././components/Logo';
import Colors from '.././constants/Colors';
import Search from '.././components/Search';
import InputForm from '.././components/InputForm';
class ASTConfig1Screen extends React.Component {
static navigationOptions = {
title: 'ASTConfig1',
};
constructor(props) {
super(props);
this.state= {
isLoading: true,
board: {},
key: '',
status:true,
text: '',
list:[
{
title: 'Getting Started',
body: 'React native Accordion/Collapse component, very good to use in toggles & show/hide content'
},
{
title: 'Components',
body: 'AccordionList,Collapse,CollapseHeader & CollapseBody'
},
{
title: 'Test',
body: 'AccordionList,Collapse,CollapseHeader & CollapseBody'
}
],
}
}
componentDidMount() {
const { navigation } = this.props;
const ref = firebase.firestore().collection('boards').doc(JSON.parse(navigation.getParam('boardkey')));
ref.get().then((doc) => {
if (doc.exists) {
this.setState({
board: doc.data(),
key: doc.id,
isLoading: false
});
} else {
console.log("No such document!");
}
});
}
_head(item){
return(
<Separator bordered style={{alignItems:'center'}}>
<Text>{item.title}</Text>
</Separator>
);
}
_body(item){
return (
<View style={{padding:10}}>
<Text style={{textAlign:'center'}}>{item.body}</Text>
</View>
);
}
ShowHideTextComponentView = () =>{
if(this.state.status == true)
{
this.setState({status: false})
}
else
{
this.setState({status: true})
}
}
render() {
if(this.state.isLoading){
return(
<View style={styles.activity}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
)
}
return (
<View style={styles.screen}>
<ImageBackground
source={require('.././assets/Img/BackGround2.png')}
style={styles.background}>
</ImageBackground>
<SafeAreaView>
<View style={{flex: 1, justifyContent: 'space-between', width: wp('80%')}}>
<View style={styles.logotop}>
<Logo/>
</View>
<View style={styles.editstock}>
<Text style={styles.editstocktext}>EDIT STOCK LIST:</Text>
</View>
{/*Start of 3 buttons Firebase*/}
<View style={styles.three}>
<View style={styles.edit1}>
<Text style={styles.textheader}>{this.state.board.name}</Text>
<Button style={styles.edit} title='Edit' ></Button>
</View>
<View style={{padding: 3}}></View>
<View style={styles.edit2}>
<Text style={styles.text}>{this.state.board.inventoryclass}</Text>
<Button style={styles.edit} title='Edit' ></Button>
</View>
<View style={styles.edit3}>
<Text style={styles.text}>{this.state.board.outlet}</Text>
<Button style={styles.edit} title='Edit' ></Button>
</View>
</View>
{/*End of 3 buttons Firebase*/}
{/*Start of AccordionList*/}
<ScrollView>
<View style={styles.middle}>
<AccordionList
list={this.state.list}
header={this._head}
body={this._body}
/>
</View>
</ScrollView>
{/*End of AccordionList*/}
{/*Start of Search*/}
<View>
{
this.state.status ? null : <View style={styles.bottom}>
<Text style={styles.textbottom}>SEARCH FOR NEW ITEMS:</Text>
<Search />
</View>
}
<Button title="ADD" onPress={this.ShowHideTextComponentView} />
</View>
</View>
</SafeAreaView>
</View>
);
};
};
{/*End of Search*/}
export default ASTConfig1Screen;
but it returns the error - Error adding document: [SyntaxError: SyntaxError: SyntaxError: JSON Parse error: Unexpected identifier "undefined"
in inputform.js saveBoard function you should
this.props.navigation.navigate('Details', {
'ASTConfig1': docRef
});
and remove JSON.parse form other file
I have created a table view using react-native list view.
I want to fix the header and first column in this table.
Here is what I created:
This list view is scrollable horizontally as well as vertically and all the data is dynamic.
I want to freeze the header and first column of this table if possible
Here is my code:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
ListView,
ScrollView,
Image,
TouchableOpacity,
Platform,
View
} from 'react-native';
import PMR from "../utilities/PMR";
import Spinner from 'react-native-loading-spinner-overlay';
import NavigationBar from 'react-native-navbar';
let bg = require('image!bg'),
back = require('image!back'),
Dimensions = require('Dimensions'),
windowSize = Dimensions.get('window'),
Screen = require('Dimensions').get('window');
class StationData extends Component {
constructor(props) {
super(props)
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
isVisible: true,
}
}
componentDidMount() {
let {username, password} = this.props.navigator.cerdentials;
let obj = {
'stXid':this.props.data.stationId,
'startTime':this.props.data.from,
'endTime':this.props.data.to
};
PMR.rawData(username, password, obj).then((rslt) => {
var RawData = [];
rslt.forEach(function(dataItem) {
if (dataItem.pointValue.length > 0)
RawData.push(dataItem);
});
RawData.unshift({name: 'Time', pointValue: rslt[0].pointValue})
// console.log(rslt)
this.setState({
dataSource: this.state.dataSource.cloneWithRows(RawData),
isVisible: false,
});
});
}
renderData(rowData) {
// console.log(rowData.pointValue);
if(rowData.name === 'Time'){
return (
<View>
<View style={{borderColor:'rgba(214, 214, 214,1)',borderWidth:1}}>
<View style={styles.names}>
<Text style={{marginHorizontal: 12, marginVertical:(Platform.OS === 'ios') ? 15 : 16}}>{rowData.name}</Text>
</View>
{rowData.pointValue.map((data) => <View style={{borderBottomWidth:1,borderColor:'rgba(214, 214, 214,1)',padding:8}}><Text>{data.time.split(" ")[1]}</Text></View>)}
</View>
</View>
);
} else {
return (
<View>
<View style={{borderColor:'rgba(214, 214, 214,1)',borderWidth:1}}>
<View style={styles.names}>
<Text style={{marginHorizontal: 12, marginVertical: 6.5}}>{rowData.name}</Text>
<Text>({rowData.engineeringUnit})</Text>
</View>
{rowData.pointValue.map((data) => <View style={{borderBottomWidth:1,borderColor:'rgba(214, 214, 214,1)',padding:8}}><Text>{isNaN(data.value) ? (data.value) : +(Math.round(data.value + "e+1") + "e-1")}</Text></View>)}
</View>
</View>
);
}
}
render() {
var titleConfig = {
title: `Station Data: ${this.props.data.stationName}`,
}
return (
<View style={styles.container}>
<Image style={styles.bg} source={bg} />
<Spinner visible={this.state.isVisible} />
<NavigationBar
title = {titleConfig}
leftButton={<TouchableOpacity onPress={() => this.props.navigator.pop()}>
<Image style={styles.back} source={back} />
</TouchableOpacity>}
style = {{backgroundColor:'rgba(244, 244, 244,1)'}} />
<ScrollView>
<View style={{position:'absolute', right:16, marginVertical:12}}>
<Text>{this.props.data.showDate}</Text>
</View>
<ListView
horizontal={true}
automaticallyAdjustContentInsets={false}
removeClippedSubviews={false}
enableEmptySections={true}
dataSource={this.state.dataSource}
renderRow={this.renderData}
style={styles.listView}
/>
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
bg: {
position: 'absolute',
left: 0,
top: 0,
width: windowSize.width,
height: windowSize.height
},
names: {
backgroundColor:'rgba(219, 243, 207,1)',
borderBottomWidth:1,
borderColor:'rgba(214, 214, 214,1)',
alignItems:'center',
},
back:
{ marginTop:10,
marginLeft:10,
width:20,
height:20,
},
listView: {
marginVertical:(Screen.width/100)*10,
marginHorizontal:(Screen.width/100)*5
},
});
module.exports =StationData;
I have created custom tabs in react-native but I am unable to select a tab. I have initialized the state for the selected tab but do not know where to set the state.
here is my code:
'use strict';
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
Image,
View
} from 'react-native';
var Dimensions = require('Dimensions');
var windowSize = Dimensions.get('window');
var bg = require('image!bg');
class TabView extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: 'list',
selectedTab: 'map'
};
}
render() {
return (
<View style={styles.container}>
<Image style={styles.bg} source={bg} />
<View style={styles.tabView}>
<View style={[styles.listView,styles.selectedView]}>
<Text>List View</Text>
</View>
<View style={[styles.listView,{}]}>
<Text>Map View</Text>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
bg: {
position: 'absolute',
left: 0,
top: 0,
width: windowSize.width,
height: windowSize.height
},
tabView: {
flexDirection: 'row',
//bottom: 200,
borderWidth:2,
borderColor:'rgba(4, 193, 3,1)',
borderRadius: 5,
marginHorizontal: 20,
marginTop: 50
},
listView: {
flex: 2,
padding:7,
alignItems:'center'
},
mapView: {
flex: 2,
padding:7,
alignItems:'center'
},
selectedView: {
backgroundColor:'rgba(4, 193, 3,1)',
color: 'white'
}
});
module.exports = TabView
I just want to know where shall I add a check so that I can make a difference in the selected tab
Any help will be appreciated.
Please, check out the code here, to get an idea how it can be done
const Tab = (props) => {
let style = props.isSelected && styles.selectedTab || styles.normalTab;
return (
<View style={style}>
<TouchableHighlight onPress={() => props.onTabPress(props.id)}>
<Text>{props.title}</Text>
</TouchableHighlight>
</View>
)
}
class TabsView extends Component {
constructor(props) {
super(props)
this.state = {
selectedTab: 'one'
}
}
render() {
return (
<View>
<Tab onTabPress={this.onSelectTab.bind(this)} title="One" id="one" isSelected={this.state.selectedTab == "one"}/>
<Tab onTabPress={this.onSelectTab.bind(this)} title="Two" id="two" isSelected={this.state.selectedTab == "two"}/>
</View>
)
}
onSelectTab(selectedTab) {
this.setState({ selectedTab })
}
}
The above code splits your component in two parts, a logical part (TabsView) and a dumb presentational part (Tab)
The logical handles the clickHandler (onSelectTab) which is passed as a prop (onTabPress) to the dumb (Tab) Component.
I just want to know where shall I add a check so that I can make a difference in the selected tab
In the render method, it should go
example:
render() {
let FirstTabStyles = Object.assign(
defaultTabStyles,
(isFirstSelected && selectedStyles || {})
)
let SecondTabStyle = Object.assign(
defaultTabStyles,
(isSecondSelected && selectedStyles || {})
)
return (
<View>
<FirstTab style={FirstTabStyle} />
<SecondTab style={SecondTabStyle} />
</View>
)
}
I am new to React Native I am making a sample app where the user can login and register for a new account.
I have two React classes,
One is the main class index.ios.js and another class called register.js. In the index class I am saying if the variable register is true render the register screen.
In the class register.js I am trying to set the variable register to false using this.setState({register:false}) but it is not causing the re render of the parent (index.ios.js). Is the a super(state) method or something similar that I am missing ? I believe the parent state is not getting the values of the updated register variable.
Here are my classes:
Render inside index.ios.js:
render: function() {
if(this.state.register) {
return this.renderRegisterScreen();
}
else if (this.state.loggedIn) {
return this.userLoggedIn();
}
else {
return this.renderLoginScreen();
}
}
Register.js:
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
Image,
TouchableHighlight,
TextInput,
} = React;
var Register = React.createClass({
render: function() {
return (
<View style={styles.container}>
<View style={styles.rafitoImage}>
<Image source={require('./logo.png')}></Image>
<Text style={styles.slogan}>Eliminate the need to wait!</Text>
</View>
<View style={styles.bottomSection}>
<View style={styles.username}>
<View style={styles.inputBorder}>
<TextInput placeholder="Username..." style={styles.usernameInput} onChangeText={(text) => this.setState({username: text})}/>
</View>
<View style={styles.inputBorder}>
<TextInput password={true} placeholder="Password..." style={styles.usernameInput} onChangeText={(text) => this.setState({password: text})}/>
</View>
<View style={styles.inputBorder}>
<TextInput password={true} placeholder="Verify Password..." style={styles.usernameInput} onChangeText={(text) => this.setState({verifyPassword: text})}/>
</View>
<View style={styles.inputBorder}>
<TextInput placeholder="Phone.." style={styles.usernameInput} onChangeText={(text) => this.setState({phone: text})}/>
</View>
<View style={styles.inputBorder}>
<TextInput placeholder="Email.." style={styles.usernameInput} onChangeText={(text) => this.setState({email: text})}/>
</View>
<TouchableHighlight style={styles.button}
underlayColor='#f1c40f' onPress={this.register}>
<Text style={styles.buttonText}>Register</Text>
</TouchableHighlight>
<TouchableHighlight style={styles.signUp} onPress={this.resetToLogin}
underlayColor='#ffffff'>
<Text style={styles.signUpText}>Already A Member </Text>
</TouchableHighlight>
</View>
</View>
<View style={styles.copyright}>
</View>
</View>
);
},
resetToLogin: function() {
this.setState({
register: false //I want this to re render the home screen with the variable register as false
});
}
});
var styles = StyleSheet.create({
container: {
flex : 1
},
bottomSection: {
flex: 5,
flexDirection: 'row'
},
button: {
height: 36,
backgroundColor: '#32c5d2',
justifyContent: 'center',
marginTop: 20
},
buttonText: {
fontSize: 18,
color: 'white',
alignSelf: 'center'
},
signUpText: {
color: '#3598dc'
},
signUp: {
alignItems: 'flex-end',
marginTop: 10,
},
username: {
flex: 1,
padding: 5
},
rafitoImage: {
flex: 3,
justifyContent: 'center',
alignItems: 'center',
},
copyright: {
alignItems: 'center'
},
usernameInput: {
height: 36,
marginTop: 10,
marginBottom: 10,
fontSize: 18,
padding: 5
},
copyrightText: {
color: '#cccccc',
fontSize: 12
},
inputBorder: {
borderBottomWidth: 1,
borderBottomColor: '#ececec'
},
slogan: {
color: '#3598dc'
}
});
module.exports = Register;
Attempt 1
As per the answer I added this to my index.ios.js
renderRegisterScreen: function() {
return (
<Register login={this.login}/>
)
}
And I added this to my register.js
<TouchableHighlight style={styles.signUp} onPress={this.props.login}
underlayColor='#ffffff'>
<Text style={styles.signUpText}>Already A Member </Text>
</TouchableHighlight>
But for some reason it does not even go to the register screen anymore, it executes the login function as soon as the register screen renders. What am I missing now ? Please advise.
Thanks
Update
It works when I pass down registered as a property but not when I do not. I would like to understand why if someone could post that.
Thanks
You can pass the function down to the child as props, then set the state of the parent from within the child that way.
Parent Component:
var Parent = React.createClass({
getInitialState() {
return {
registered: false
}
},
register(){
console.log("logging in... ");
this.setState({
registered: true
});
},
render: function() {
return (
<View style={styles.container}>
<Child register={this.register.bind(this)} registered={this.state.registered} />
{this.state.registered && <View style={{padding:10, backgroundColor:'white', marginTop:10}}>
<Text style={{fontSize:20}}>Congratulations, you are now registered!</Text>
</View>}
</View>
);
}
});
Child Component:
var Child = React.createClass({
render: function() {
return(
<View style={{backgroundColor: 'red', paddingBottom:20, paddingTop:20 }}>
<TouchableHighlight style={{padding:20, color: 'white', backgroundColor: 'black'}} onPress={() => this.props.register() }>
{this.props.registered ? <Text style={{color: 'white'}}>registered</Text> : <Text style={{color: 'white'}}>register</Text>}
</TouchableHighlight>
</View>
)
}
})
Here is a more powerful solution. This will let the child component change any state variable in the parent.
Parent component:
render: function() {
return (
...
<Child setParentState={newState=>this.setState(newState)} />
...
);
}
// Take note of the setState()
Child component:
this.props.setParentState({registered: true})
Why my attempt was failing was because I was using
onPress={this.props.login}
It should be
onPress={()=>this.props.login}
because of that mistake my onPress function would execute as soon as the button would render. I am not sure why that happens but I know what my mistake was.
Using StackNavigator I found a soultion leveraging screenProps. Here you can pass down functions and values to your routes. App global state is managed in App. App then passes in functions and/or state to NavComponent screenProps. Each child route in StackNavigator will then have access via this.props.screenProps
This solution is working well for now. Would love some feedback, or suggestions for improving this method
class HomeScreen extends React.Component {
render() {
return (
<View>
<Text>{JSON.stringify(this.props.screenProps.awesome)}</Text>
<Button
onPress={() => this.props.screenProps.updateGlobalState("data")}
title="Update parent State"
/>
</View>
);
}
}
const NavComponent = StackNavigator({
Home: { screen: HomeScreen },
// AllOthers: { screen: AllComponentsHereMayAccessScreenProps },
});
export default class App extends React.Component {
constructor() {
super();
this.state = {
everythingIsAwesome: false,
}
}
_updateGlobalState(payload) {
console.log('updating global state: ', payload);
this.setState({everythingIsAwesome: payload});
}
render() {
return <NavComponent screenProps={{
updateGlobalState: this._updateGlobalState.bind(this),
awesome: this.state.everythingIsAwesome
}} />;
}
}