I am totally lost on how to grab two parameters from a form screen and pass them via React Navigator and display them on the previous screen.
The app section in question works like this:
1. touchablehighlight to form screen.
2. input title and description and press submit onpress
3. the onpress runs a function that dispatches the parameters to the previous page via a key.
4. then returns back to the origin page, with the props on display.
I am having multiple issues with the process:
1. if I am understanding the docs correctly, each page has a unique key and i tried to find it via this.props.navigation.state.key, however unknown to me, on refresh the id number would change.
2. that leads to problem 2 where the function will run, but it will not redirect back to the original page.
3. i have tried .navigate line after .dispatch but it would open a new copy of the original page and not display the new props that supposively were passed down.
import React from 'react';
import styles from '../styling/style.js';
import { Text, View, Image, TouchableHighlight, TextInput, Alert } from 'react-native';
import { StackNavigator, NavigationActions } from 'react-navigation';
import Forms from './formGenerator';
export default class Workout extends React.Component {
constructor(props) {
super(props);
this.state = {
programTitle: '',
programDescription: ''
}
}
render() {
const {navigate} = this.props.navigation;
return (
<Image style={styles.workoutContainer, { flex: 1}} source={require("../images/HomepageBackground.jpg")}>
<View style={styles.workoutBody}>
<Text style={styles.workoutTextBody}> {this.state.programTitle}</Text>
<Text style={styles.workoutTextBody}>{this.state.programDescription}</Text>
</View>
<View style={styles.createButton}>
<TouchableHighlight onPress={Alert.alert(this.props.navigation.state.key)} style={styles.addButtonTouch} title='test' >
<Text style={styles.addButtonText}>+</Text>
</TouchableHighlight>
</View>
</Image>
);
}
// End of the render
}
import React from 'react';
import styles from '../styling/style.js';
import { Text, View, Image, TouchableHighlight, TextInput, Button, Alert } from 'react-native';
import Workout from './workouts';
import { NavigationActions } from 'react-navigation';
export default class Forms extends React.Component {
constructor(props) {
super(props);
this.state = {
programTitle: '',
programDescription: ''
}
}
render() {
const {goBack} = this.props.navigation;
const {params} = this.props.navigation.state;
return (
<Image style={styles.workoutContainer, { flex: 1}} source={require("../images/HomepageBackground.jpg")}>
<View style={styles.workoutBody}>
<Text style={styles.formHeader}>Program Title</Text>
<TextInput
autoFocus={true}
style={styles.formBody}
onChangeText={(programTitle) => this.setState({programTitle})}
placeholder='Title'
value={this.state.programTitle} />
<Text style={styles.formHeader}>Description (Ex. 4sets x 10reps)</Text>
<TextInput
autoFocus={true}
style={styles.formBody}
placeholder='Description'
onChangeText={(programDescription) => this.setState({programDescription})}
value={this.state.programDescription} />
<TouchableHighlight onPress={this.addProgram} style={styles.buttonBody} title="Add Program" >
<Text>Add Program</Text>
</TouchableHighlight>
</View>
</Image>
);
}
addProgram = () => {
Alert.alert(this.props.navigation.state.key);
this.setState({programTitle: ''});
this.setState({programDescription: ''});
const setParamsAction = NavigationActions.setParams({
params: { programTitle: this.state.programTitle, programDescription: this.state.programDescription },
key: ,
})
this.props.navigation.dispatch(setParamsAction)
};
}
If you are trying to get parameter from "Next Page", you could have two approaches.
1, save the params in AsyncStorage (suggested)
2, using navigation setParams function with the params
const setParamsAction = NavigationActions.setParams({
params: { title: 'Hello' },
key: 'screen-123',
})
this.props.navigation.dispatch(setParamsAction)
https://reactnavigation.org/docs/navigators/navigation-actions
You are just trying to display information from the Forms class on the Workout class, correct??
From your Workout class, create a function that update's it's state.
updateWorkoutState = (programTitle,programDescription) => this.setState(programTitle,programDescription)
Pass that function through to the Forms class when you push that route.
this.props.navigation.navigate('Forms',{updateWorkoutState: this.updateWorkoutState}
Once your conditions are met on the Forms class and you want to update the Workout component, call it with this.props.navigation.state.params.updateWorkoutState(val1,val2)
Do not use AsyncStorage for this.
Related
i need help :).
my project is 2 pages in react native, MainPage and SoundRecord.
my init screen is MainPage and when i press the button 'take sound'
i move to another component to record sound(i move with react native navigation).
when i come back i want to return the filePath(where it save the file..).
i want to insert it to the state.
when i do this in MainPage:
this.state{
filePath: this.props.navigation.state.params.filePath
}
it give error:
undefined is not an object(evaluating 'this.props.navigation.state.params.filePath')
and i understand this because i start my project with MainPage and i dont have the filePath from the SoundRecord page.
what can i do?
can i do check if this.props.navigation.state.params.filePath !== undefined?
how to do it? i try almost everything...
i put the relevant code:
MainPage:
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import ImagePicker from 'react-native-image-picker';
import { RNS3 } from 'react-native-aws3';
import { aws } from './keys';
import SoundRecord from './SoundRecord'
export default class MainPage extends Component {
constructor(props){
super(props);
this.state={
file:'' ,
config:'',
filePath: '',
fileName: '',
tag:''
};
}
MoveToSoundRecordPage = () => {
this.props.navigation.navigate('SoundRecord');
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this.MoveToSoundRecordPage}>
<Text>take sound</Text>
</TouchableOpacity>
{/* <SoundRecord takeSound={this.takeSound}/> */}
<Text>{this.state.fileName}</Text>
<TouchableOpacity onPress={this.UploadToAWS.bind(this)}>
<Text>Upload To Aws</Text>
</TouchableOpacity>
</View>
);
}
}
SoundRecord when i finish to record i send the filePath like this:
finishRecord = (filePath) => {
this.props.navigation.navigate('MainPage',{filePath});
}
thank!
If you want to update something on the previous page then you can do it in the following way.
Create a function in the MainPage that updates the state with the new filepath.
Pass the function as as a param.
Use the passed function in SoundRecord to update the state in the MainPage
MainPage
In your MainPage add the following
sendFilepath = (filePath) => {
this.setState({filePath})
}
Update the MoveToSoundRecordPage function to take the sendFilepath as a parameter:
MoveToSoundRecordPage = () => {
this.props.navigation.navigate('SoundRecord', { sendFilepath: this.sendFilepath });
}
SoundRecord
Then in the SoundRecord page you want to update the finishRecord so that it calls the function that you have passed.
finishRecord = (filePath) => {
this.props.navigation.state.params.sendFilepath(filePath)
this.props.navigation.goBack();
}
Snack
Here is a snack https://snack.expo.io/#andypandy/navigation-passing-function that shows passing a function called sendFilepath from Screen1 to Screen2. Then in Screen2 it then calls the function that was passed and this updates the state in Screen1.
Please try this. It may help you
Add this in MainPage screen:
componentWillMount() {
const filePath = this.props.navigation.getParam('filePath', '');
this.setState({ filePath:filePath })
}
I have implemented a pop-up component in a header with More Options button. This button is available only on the UserProfileScreen. When you press on it you see two buttons: 'Settings' and 'Log out'. The issue is related to Settings button.
Expected behavior:
Press on Settings button -> this.props.navigation.navigate('SettingsNavigator') is called -> SettingsScreen is shown
Actual behavior:
Press on Settings button -> console.warn('go to settings') is called and nothing more (you are not redirected to SettingsScreen).
SettingsNavigator.js
import React from 'react';
import { createStackNavigator } from 'react-navigation';
import SettingsScreen from './SettingsScreen';
import EditProfileScreen from './EditProfileScreen';
import RemoveProfileScreen from './RemoveProfileScreen';
const SettingsNavigator = createStackNavigator({
SettingsScreen: SettingsScreen,
EditProfile: EditProfileScreen,
RemoveProfile: RemoveProfileScreen
}, {
initialRouteName: 'SettingsScreen'
});
export default SettingsNavigator;
optionHeaderButton.js
import { withNavigation } from 'react-navigation';
...
class OptionsHeaderButton extends Component {
...
navigateToSettings = () => {
console.warn('go to settings')
this.props.navigation.navigate('SettingsNavigator');
}
render() {
return(
<Menu onSelect = {value => {
switch(value) {
case 'Settings':
this.navigateToSettings();
break;
case 'Logout':
this.onLogOutPress();
break;
}
}}>
<MenuTrigger>
<Icon name = 'dots-vertical' size = {24} color = {text}/>
</MenuTrigger>
<MenuOptions style = {optionsHeaderButtonStyle.menuWrapper}>
<MenuOption value = {'Settings'}>
<View style = {optionsHeaderButtonStyle.optionWrapper}>
<View style = {optionsHeaderButtonStyle.optionIcon}>
<IconSimpleLine name = 'settings' size = {12} color = {text}/>
</View>
<Text style = {[optionsHeaderButtonStyle.option, optionsHeaderButtonStyle.lastOption]}>
Settings
</Text>
</View>
</MenuOption>
<MenuOption value = {'Logout'}>
<View style = {optionsHeaderButtonStyle.optionWrapper}>
<View style = {optionsHeaderButtonStyle.optionIcon}>
<IconSimpleLine name = 'logout' size = {12} color = {text}/>
</View>
<Text style = {[optionsHeaderButtonStyle.option, optionsHeaderButtonStyle.lastOption]}>
Logout
</Text>
</View>
</MenuOption>
</MenuOptions>
</Menu>
)
}
}
export default withNavigation(OptionsHeaderButton);
OptionsHeaderButton is nested to a Header Component that is nested in UserProfile Component.
As far as I know using withNavigation() I can call navigation.navigate() from any component. So why doesn't it work?
UPDATE:
After investigation I found that this issue is related to a bug of React Navigation that was found on April of 2017. It means that you can use navigation.navigate() only inside one Stack. But I can't find any info about fix of this bug.
you need to pass screen name instead of navigator.
this.props.navigation.navigate('SettingsSecreen');
try this:
import { NavigationActions, withNavigation } from 'react-navigation';
navigateToSettings = () => {
const navigateAction = NavigationActions.navigate({ routeName: 'SettingsScreen' });
this.props.navigation.dispatch(navigateAction);
}
I had this issue but I have wired up my App.js slightly differently using stack navigation and drawer navigation (And switch navigation for auth) - mostly followed the react navigation docs:
App.js
const AuthStack = createStackNavigator({ Login: Login }, { headerMode: 'none' });
const ActivityStack = createStackNavigator({ Activity: ActivityScreen}, {headerMode: 'none'});
// setup my routing & config here
export default createAppContainer(createSwitchNavigator(
{
Auth: AuthStack,
ActivityStack: ActivityStack,
},
{
intitalRouteName: 'Auth',
},
));
In the screen I want to navigate from I wire up the constructor like this (to bind the navigation function):
FirstScreen.js
export default class FirstScreen extends React.Component {
constructor(props) {
super(props);
this.navigateToScreen = this.navigateToScreen.bind(this);
}
state = {
// state items here
}
navigateToScreen(screenName) {
this.props.navigation.navigate(screenName);
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button} onPress=
{this.navigateToScreen("Activity")}>
<Text style={styles.loginText}>
ADD ACTIVITY
</Text>
</TouchableOpacity>
</View>
Note how I am calling the navigation, I am only passing in the route alias I gave earlier in the App.js file:
Activity: ActivityStack,
So I would write this.navigateToScreen("Activity") from the calling point passing in the alias not the screen name or stack name.
This navigates correctly to the screen and utilizes stack navigation I defined earlier in App.js.
Hope this makes sense (parent child hierarchy is achieved by nesting the various navigation features like switch, stack and drawer to achieve your desired results.
in react-navigation Version: 5.x withNavigation is not working. To fix this issue try this.
import { useNavigation } from '#react-navigation/native';
const navigation = useNavigation();
navigation.navigate("DetailsScreen")
no need to warp the component in withNavigation.
Hope this may helps.
I am kind of stuck with it, I just can't get it working properly, I've found couple of stack overflow links too, it seems that syntax is correct.
setEvent gets parameters, this version will freeze the app, if I destruct or send only couple of fields from object, it will say, that those are undefined.
Any help, I would be happy, can't figure out the mistake myself.
Line, that should send data <TouchableOpacity onPress={(item) => this.setEvent(item)}>
one item is just one object with properties id and name.
import React from 'react';
import {View, FlatList, ActivityIndicator, Text, TouchableOpacity} from 'react-native';
import {server, defaultRequestSettings} from "../configuration";
import {styles} from '../styles/home_screen';
export default class HomeScreen extends React.Component {
state = {
loading: true
};
constructor(props) {
super(props);
this.setEvent = this.setEvent.bind(this);
}
componentDidMount() {
fetch(`${server}/data`, defaultRequestSettings)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
loading: false,
dataSource: responseJson.events,
});
});
}
setEvent (item) {
console.log(item)
};
render() {
if (this.state.loading) {
return (
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return (
<View style={styles.container}>
<FlatList
data={this.state.dataSource}
renderItem={({item}) =>
<TouchableOpacity onPress={(item) => this.setEvent(item)}>
<Text style={styles.item}>{item.name}</Text>
</TouchableOpacity>
}
keyExtractor={({id}) => `list-item-${id}`}
/>
</View>
);
}
}
define your state like this in constructor
constructor(props) {
super(props);
this.state = {
dataSource: [],
}
this.setEvent = this.setEvent.bind(this);
}
in this way initial state of dataSource would be an empty array, you also should use lodash, which would further save you from undefined response from server like this
import _ from 'lodash'
const dataSource = _.get(this.state, 'dataSource', [])
UPDATE
remove item from onPress like this
onPress={() => this.setEvent(item)
because it takes item as a callback object which is returned by the TouchableOpacity instead you need the object from FlatList.
I'm new to RN, trying to get around it by trial and erroring a lot. I'm currently stuck with this :
I have one parent view which is like this :
import React, { Component } from 'react';
import { View, Image, Text, Button } from 'react-native';
class ParentView extends Component {
render() {
return(
<View style={{flex:1}}>
<View style={{flex:0.14, flexDirection:'row', alignItems:'center'}}>
<View style={{flex:1}}>
<Image
source = {require('./assets/image1.png')}
resizeMode= 'contain'
style={{flex:1, height:null, width:null}}
/>
</View>
<View style={{flex:3}}>
<Button title='dummytitle' onPress={() => this.props.navigation.navigate('Child', {
dbpath: 'db.category.subcategory',
})}
/>
</View>
etc...
This part works OK. In child view, I'm trying to import JSON data from a file like so :
import React, { Component } from 'react';
import { Text, View, TouchableOpacity, Image, StyleSheet } from 'react-native';
import AwesomeAlert from 'react-native-awesome-alerts';
import db from './db/quizDB';
class Quiz extends Component {
constructor(props) {
super(props);
this.qno = 0
this.score = 0
quiz = this.props.navigation.getParam('dbpath');
arrnew = Object.keys(quiz).map(function(k) {return quiz[k]});
this.state = {
question: arrnew[this.qno].question,
}
};
render() {
return(
<View style={{flex:1}}>
<View style={{flex:2}}>
<View style={styles.Question}>
<Text>{this.state.question}</Text>
</View>
etc..
{this.state.question} returns nothing, it's just empty. But if I hardcode quiz as quiz = db.category.subcategory, it does work, {this.state.question} displays the expected content.
What am I missing there ? It seems like props aren't processed as I'd like them to...
You need to either declare quiz and arrnew with let or var, or attach them to the state.
In addition, in React, standard practice is not to directly attach properties to the class instance, like you've done here:
this.qno = 0
this.score = 0
These should probably be local variables, but if you need to, these could be attached to the state instead.
Solved this, I was being stupid. I just imported db in the Parent view and set the quiz prop directly from there. My mistake was to set it as a string...
On my React Native app I am initially loading SceneA, which simply displays a text "Click me". When this button is clicked I would like to load a completely different screen (SceneB) that has completely different components.
All the examples that I found online (like the example below) load a scene with exactly the same components, but different values. In my case it is a completely different layout. How can I navigate to that new screen (SceneB)?
It is basically the equivalent to a new Activity on Android, passing some data at the same time. Any pointers will be appreciated.
index.android.js
import React, { Component } from 'react';
import { AppRegistry, Navigator } from 'react-native';
import SceneA from './SceneA';
class ReactNativeTest extends Component {
render() {
return (
<Navigator
initialRoute={{ title: 'My Initial Scene', index: 0 }}
renderScene={(route, navigator) =>
<SceneA
title={route.title}
// Function to call when a new scene should be displayed
loadNewScene={() => {
const nextIndex = route.index + 1;
navigator.push({
title: 'Scene B',
index: nextIndex,
});
}}
/>
}
/>
)
}
}
AppRegistry.registerComponent('ReactNativeTest', () => ReactNativeTest);
SceneA.js
import React, { Component, PropTypes } from 'react';
import { View, Text, TouchableHighlight } from 'react-native';
export default class SceneA extends Component {
render() {
return (
<View>
<Text>Scene A</Text>
<TouchableHighlight onPress={this.props.loadNewScene}>
<Text>Click Me</Text>
</TouchableHighlight>
</View>
)
}
}
SceneA.propTypes = {
loadNewScene: PropTypes.func.isRequired,
};
You handle which components should render in the renderScene function, each scene will have a route.title so you can decide which to render based on that.
renderScene={(route, navigator) =>
if (route.title === 'Scene A') {
return <SceneA navigator={navigator} />
}
if (route.title === 'Scene B') {
return <SceneB navigator={navigator} />
}
}
Then inside your components you're gonna have a function that handles the navigation:
navigateToSceneB = () => {
this.props.navigator.push({
title: 'Scene B'
})
}