How to keep the previous state after navigation - react-native

//Home page
class Home extends Component {
constructor(props){
super(props);
this.state = {time: ""}
}
render(){
const { navigate } = this.props.navigation;
return(
<View>
<TextInput onChangeText={(time) => this.setState({ time })} placeholder = "enter time" />
<TouchableOpacity style={{marginTop: 10, height: 20, width: 50, borderRadius: 6, borderColor: 'white', backgroundColor:'red'}}
onPress = { () => {
navigate('Wait', {
time: this.state.time * 1000
})
}}
>
<Text style={{color: 'white'}}>add</Text>
</TouchableOpacity>
</View>
)
}
}
//Waitlist page
class Wait extends Component{
constructor(props){
super(props);
this.state = { data: []}
}
renderTime(){
return this.state.data.map((item) => {
return(
<View>
<TimerCountdown
initialSecondsRemaining={ item }
allowFontScaling={true}
style={{ fontSize: 20 }}
/>
</View>
)
})
}
updatestate(time){
var temp = this.state.data
temp.push(time)
this.setState({data: temp})
}
render(){
const { navigate } = this.props.navigation;
const { navigation } = this.props;
const time = navigation.getParam('time', 'NO-Name');
return(
<View>
<TouchableOpacity onPress = {()=>{ this.updatestate(time)}}>
<Text> Update </Text>
</TouchableOpacity>
<View>
{this.renderTime()}
</View>
</View>
)
}
}
I am trying to write a function to keep adding countdown timer to another page. After the user enter the input time, it should navigate to the waitlist page and update the state and render all the timers. The above code does not work because each time I navigate to that page the state will reset.
I also tried it with the AsyncStorage. I was able to add the time to the AsyncStorage on the home page and render each time on the waitlist page. But the problem is all the timers will reset every time I add a new timer.
I just want to know how can I keep the previous state after navigate to another page. Can someone please show me some examples? Thanks a lot.
Documentation for timer-countdown: https://www.npmjs.com/package/react-native-timer-countdown

Related

How to pass state between screens in React Native

This is screen 1, where I am supposed to be passing the state for button text.
export class EditProfile extends Component {
constructor(props) {
super(props);
this.navigateToName = this.navigateToName.bind(this);
this.navigateToAboutMe = this.navigateToAboutMe.bind(this);
this.navigateToInterests = this.navigateToInterests.bind(this);
this.state = {
showing: true,
aboutUser: {
buttonText: "hey",
showing: false,
},
};
}
navigateToName = () => {
this.props.navigation.navigate("CreateProfile", {
showing: false,
});
};
navigateToAboutMe = () => {
this.props.navigation.navigate("AboutUser", {
aboutUser: this.state.aboutUser,
});
};
navigateToInterests = () => {
this.props.navigation.navigate("Interests", { showing: false });
};
render() {
return (
<View>
<View style={ProfileEditStyle.justifyImage}>
<Image
style={ProfileEditStyle.image}
source={require("../../Graphics/jessica_alba.jpg")}
/>
</View>
<View>
<Text style={ProfileEditStyle.basictext}>Basic Info</Text>
<TouchableOpacity style={ProfileEditStyle.buttons}>
<Text style={ProfileEditStyle.text} onPress={this.navigateToName}>
Edit Name
</Text>
</TouchableOpacity>
<TouchableOpacity style={ProfileEditStyle.buttons}>
<Text style={ProfileEditStyle.text}>Edit Photo</Text>
</TouchableOpacity>
<TouchableOpacity style={ProfileEditStyle.buttons}>
<Text style={ProfileEditStyle.text}>Edit Location</Text>
</TouchableOpacity>
<TouchableOpacity
style={ProfileEditStyle.buttons}
onPress={this.navigateToAboutMe}
>
<Text style={ProfileEditStyle.text}>Edit About Me</Text>
</TouchableOpacity>
<TouchableOpacity
style={ProfileEditStyle.buttons}
onPress={this.navigateToInterests}
>
<Text style={ProfileEditStyle.text}>Edit Interests</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
export default EditProfile;
This is screen 2, where I want the state for the buttonText to change on the continue button component.
export class AboutUser extends Component {
constructor(props) {
super(props);
this.navigatToInterests = this.navigatToInterests.bind(this);
this.checkEntry = this.checkEntry.bind(this);
var params = props.navigation.state.params.aboutUser;
this.state = {
value: "",
};
}
<ContinueButton
text={this.props.route.params.aboutUser.buttonText}
color="#ff304f"
style={CreateAboutMe.centerButton}
onPress={this.navigatToInterests}
/>
I am trying to do conditional rendering for the continue button component. When its on one screen I want it to say "continue" and when its on another route I want it to say " Save and Go Back". However when I try to change the state between screens for some reason I either get a params error or the state doesn't change.
Get parameter from previous screen :
constructor(){
const isShowBtn = this.props.navigation.getParam("showing");
this.state = {
value: isShowBtn
}
}

Maximum update exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate

I'm new to React Native and no background in reactjs.
I'm always getting an error: "Maximum update exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops."
As you can see in the below code I want to switch watchlist on a button press. The error occurs eventhough the screen was not loaded therefore no onPress event. I'm also wondering what's the correct way to update the state. What's wrong in my trigger to update state?
export default class OverviewView extends React.Component {
constructor(props){
super(props);
this.state ={ isLoading: true,
watchlist: 'top20',
curr: 'usd'
}
}
async componentDidMount(){
const lists = ["portfolio","watchlist"];
const list_name = this.state.watchlist;
const curr = this.state.curr;
const url = 'http://sample.com/api/watchlists/'+list_name+'/overview?curr='+curr;//sample.com
is a sample domain but the URI applies
const response = await fetch(url);
const data = await response.json();
this.setState({
isLoading: false,
list_names: lists,
dataSource: data,
head: ['Coin','Price','Volume(24H)'],
})
}
render(){
const tableData = this.state.dataSource;
const lists = this.state.list_names;
if(this.state.isLoading){
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return(
<View style={styles.container}>
<View style={{flex: 1, flexDirection: 'row'}}>
{
lists.map((name,i) => (
<Button key={i}
style={[styles.text]}
onPress={this.setState({
watchlist: name,
curr: 'btc'
})}
>{name}</Button>
))
}
<Table>
<TableWrapper>
<Row data={this.state.head} textStyle={styles.text} style={[styles.row,
{backgroundColor: '#333333'}]}/>
{
tableData.map((row,index) => (
<Row key={index}
data={[row.coin,row.price,row.volume]}
textStyle={styles.text}
style={[styles.row, index%2 && {backgroundColor: '#333333'}]}
/>
))
}
</TableWrapper>
</Table>
</View>
);
}
}
As you have written your onpress , it execute at the beginning of your view rendering, it does not wait for the button to be pressed.Change it like this,
onPress={()=>{
this.setState({
watchlist: name,
curr: 'btc'
})}}

why does the touchable opacity onPress not trigger?

I am having an issue where my onPress of a touchable opacity doesn't fire. I am sure it isn't working because nothing console logs to the system when I press it.
My button component:
const FloatingPlusButton = (props) => {
return (
<View style={styles.buttonStyle}>
<TouchableOpacity onPress={props.tapToAddEvent}>
<MaterialIcons
name='add'
size={45}
color='#28313b'
/>
</TouchableOpacity>
</View>
);
};
Where I call it:
class HomeScreen extends Component {
constructor() {
super();
this.state = {
textInput: '',
inputVisible: true
};
}
onFloatingButtonPress() {
this.setState({ inputVisible: true }, () => { this.textInputField.focus(); });
console.log('p');
}
render() {
return (
<View style={{ flex: 1, height: HEIGHT }}>
{ !this.state.inputVisible &&
<FloatingPlusButton tapToAddEvent={this.onFloatingButtonPress.bind(this)} />
}
</View>
);
}
}
To be clear I do see the button and the inputVisible prop is not the issue. Just nothing happens when I press it. I tried it with both the .bind(this) and without it and neither worked.
I was able to figure it out. I had to make the zIndex of the button greater than the zIndex of the root view component. Thanks for the help everybody.

How do I disable touchableOpacity?

I am making an appointment app, I have 6 time slots on a given day. The selected slots are stored into a const called "notAvailableSlots" as soon as the component loads. How do I disable the touchableOpacity if "notAvailableSlots" has corresponding values in it, meaning someone has clicked one of the 6 slots already? I know it takes a boolean, but stuck thinking what value to pass...
const availableHours = {
timeSlots: {
slot1: "2:00pm to 2:30pm",
slot2: "2:30pm to 3:00pm",
slot3: "3:00pm to 3:30pm",
slot4: "3:30pm to 4:00pm",
slot5: "4:00pm to 4:30pm",
slot6: "4:30pm to 5:00pm"
}
};
class Appointment extends Component {
constructor(props) {
super(props);
this.state = {
datePicker: false,
date: new Date()
};
}
componentDidMount() {
this.props.fetchDataFromHeroku();
}
render() {
const availability = availableHours.timeSlots;
const notAvailableSlots = this.props.date
.filter(date => {
const month = new Date(date.date).getMonth();
const day = new Date(date.date).getDate();
const year = new Date(date.date).getFullYear();
return (
month === this.state.date.getMonth() &&
day === this.state.date.getDate() &&
year === this.state.date.getFullYear()
);
})
.map(date => date.time_slot);
// console.log(this.state.date);
console.log("not available: ", notAvailableSlots);
return (
<View style={styles.container}>
<Text style={{ backgroundColor: "#00CED1", height: 35 }}>
Hi! Please click on "calendar" to setup an appointment
</Text>
<View>
<Button
style={styles.buttonOne}
title="Make an appointment"
onPress={() => {
const { action, year, month, day } = DatePickerAndroid.open({
date: new Date()
}).then(response => {
this.setState({
datePicker: true,
date: new Date(response.year, response.month, response.day)
});
response.month += 1;
console.log("date", response);
});
}}
/>
{this.state.datePicker
? Object.keys(availability).map((time, i) => {
// console.log(time); this returns slots 1 thru 6
return (
<View key={i} style={styles.slotButton}>
<TouchableOpacity
disabled={true}
onPress={() =>
this.props.addTimeSlotToDatabase(
time,
this.props.user.id,
this.state.date
)
}
>
<Text style={{ alignItems: "center" }}>
{availability[time]}
</Text>
</TouchableOpacity>
</View>
);
})
: null}
</View>
</View>
);
}
}
Simply put the value in the state.
constructor(props) {
super(props);
this.state = {
disabled: false
};
}
render() {
return(
<TouchableOpacity disabled={this.state.disabled}>
<Text>Click</Text>
</TouchableOpacity>
)
}
Sometimes I do like this
import { TouchableOpacity, View } from 'react-native';
const MainView = isDisabled ? View : TouchableOpacity;
return (
<MainView onPress={}>

Change screen FlatList onPress

I have two screen.
First screen is HomeScreen, second screen is ProfileScreen.
I used FlatList on HomeScreen and i wanna push to navigation to another screen. But when i used that codes, i saw that error message: "Can not read property 'navigate' of undefined"
Code like that
class ProfileScreen extends Component {
static navigationOptions = {
title: 'Profile',
};
render() {
const { navigate } = props.navigation;
return <Text>Hello, I am profile!</Text>;
}
}
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
};
constructor(props) {
super(props);
this.state = {
data: [],
};
}
getScreen() {
this.props.navigation.navigate('Profile')
}
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<TouchableHighlight underlayColor= 'transparent' onPress= {this.getScreen}>
<View style= {{width: 300, height: 'auto'}} >
<Text> {item.title} </Text>
<View style= {{width: 300, height: 1, backgroundColor: 'red', marginBottom: 30, marginTop: 15}} />
</View>
</TouchableHighlight>
)}
/>
</View>
);
}
}
const AppNavigator = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen }
});
You're losing the context of this in your implementation. Fix it with function call:
renderItem={({ item }) => (
<TouchableHighlight underlayColor='transparent' onPress={() => this.getScreen()}>
...
</TouchableHighlight>
)}
In addition, you can use pattern:
const { navigate } = this.props.navigation;
navigate('Profile');
Inside the constructor bind this to your getScreen method.
Simply add following line inside constructor.
this.getScreen = this.getScreen.bind(this);