Navigate to a new screen by calling a function onPress - react-native

I would like to use my onLogin() function to navigate to the Dashboard view of the Android MVP I'm building. I've been thrown into a React Native project despite not knowing it and I've only just begun my career, so the answer is probably painfully obvious, but after lots of research I can't quite work it out! Hopefully one of you can guide me to the solution.
I've copied the Login view below.
import React, { Component } from 'react';
import {
TouchableHighlight,
TextInput,
Text,
View,
Image
} from 'react-native';
import styles from "./../style/CustomStylesheet";
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
Email: '',
password: '',
};
}
onLogin() {
const { Email, password } = this.state;
//insert function to navigate to dashboard here?
}
render() {
return (
<View style={styles.container}>
<Image
style={styles.logo}
source={require('./../../android/app/src/main/res/drawable/login.png')}
/>
<TextInput
value={this.state.Email}
onChangeText={(Email) => this.setState({ Email })}
placeholder={'Email'}
placeholderTextColor={'#333333'}
style={styles.input}
inlineImageLeft='login_id'
/>
<TextInput
value={this.state.password}
onChangeText={(password) => this.setState({ password })}
placeholder={'Password'}
placeholderTextColor={'#333333'}
secureTextEntry={true}
style={styles.input}
inlineImageLeft='login_password'
/>
<View style={styles.help}>
<Text>Need help?</Text>
</View>
<TouchableHighlight
style={[styles.input, styles.button]}
onPress={this.onLogin.bind(this)}>
<Text style={styles.buttonText}>LOGIN</Text>
</TouchableHighlight>
<View style={styles.modal}>
<Text style={styles.modalText}>New user?</Text>
<Text style={styles.modalText}>Register on our web app or our iPad app</Text>
</View>
</View>
);
}
}
I've added the dashboard to my Stack Navigation file too. Any guidance you could give would be wonderful. Many thanks!
import Login from "./Login";
import Dashboard from "./Dashboard";
const AppNavigator = createStackNavigator(
{
Home: { screen: Login },
Dashboard: { screen: Dashboard }
},
{
headerMode: 'none'
}
);
export default createAppContainer(AppNavigator);

add this
onLogin=()=> {
const { Email, password } = this.state;
const { navigation } = this.props;
navigation.navigate('Dashboard');
}

Related

React Native Redux - Not showing updated state via different route

I'm writing a react-native app via expo and trying to implement redux. I'm not sure if I'm going about this completely the wrong way. I have a home page that has two sections, a search text box and an area with links to content pages. The content pages also contain the same search box component. I want to be able to pass the contents of the search box input to the content page so that the user doesn't need to enter this again (and will probably require access to this content further in the user journey)
My app.js looks like the below:
import React from 'react';
import 'react-native-gesture-handler';
import { createStackNavigator } from 'react-navigation-stack';
import { createAppContainer } from 'react-navigation';
import ContentPage from './pages/ContentPage.js';
import LogoTitle from './components/LogoTitle';
import EventsListPage from './pages/EventsListPage.js';
import EventPage from './pages/EventPage';
import VenuePage from './pages/VenuePage';
import HomeScreen from './pages/HomePage';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
const initialState ={
postcode:"abcefg"
}
const reducer = (state = initialState, action) => {
switch(action.type)
{
case 'SET_POSTCODE':
return {
postcode: action.text
}
default:
console.log("returning default state")
return state
}
}
const store = createStore(reducer);
const RootStack = createStackNavigator(
{
Home: HomeScreen,
ContentPage: ContentPage,
EventsListPage: EventsListPage,
EventPage: EventPage,
VenuePage: VenuePage
},
{
initialRouteName: 'Home',
defaultNavigationOptions: {
headerTitle: () => <LogoTitle />,
headerLeft: () => null,
headerStyle: {
backgroundColor: '#ADD8E6'
}
},
}
);
const AppContainer = createAppContainer(RootStack);
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<AppContainer />
</Provider>);
}
}
Homepage.js:
import React from 'react';
import { StyleSheet, View } from 'react-native';
import SearchBox from '../components/SearchBox'
import TypeDrillDownArea from '../components/TypeDrillDownArea'
import 'react-native-gesture-handler';
class HomeScreen extends React.Component {
constructor(props) {
super(props);
state = {
};
}
render() {
return (
<View style={styles.container}>
<SearchBox navigation={this.props.navigation} eventTypeId=''/>
<TypeDrillDownArea navigation={this.props.navigation} />
</View>
);
}
}
export default HomeScreen
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'stretch'
},
});
Relevant searchbox.js:
render() {
return (
<ImageBackground source={topperBackground} style={{width: '100%'}}>
<View>
<View style={styles.row}>
<View>
<TextInput
value={this.props.postcode}
autoCapitalize="characters"
style={styles.inputBox}
placeholder="Enter Postcode"
onChangeText={(e) => this.props.setPostcode(e)}
/>
</View>
<View>
<TouchableOpacity
disabled={this.state.locationDisabled}
onPress={() => {
this.props.navigation.navigate('EventsListPage', {
navigation: this.props.navigation,
eventTypeId: this.state.eventTypeId,
locLongitude: this.state.location.coords.longitude,
locLatitude: this.state.location.coords.latitude,
});
}}>
<Image
style={styles.locationPin}
source={locationPin}
/>
</TouchableOpacity>
</View>
</View>
<View style={styles.searchButtonArea}>
<TouchableOpacity
onPress={() => {
console.log("postcode is: " + this.state.postcode)
this.props.navigation.navigate('EventsListPage', {
eventTypeId: this.state.eventTypeId,
postcode: this.props.postcode,
});
}}>
<Text style={styles.searchButton}>SEARCH</Text>
</TouchableOpacity>
</View>
</View>
</ImageBackground>);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SearchBox)
function mapStateToProps(state){
return {
postcode:state.postcode
}
}
function mapDispatchToProps(dispatch){
return{
setPostcode : (e) => dispatch({
type: 'SET_POSTCODE',
postcode : e
})
}
}
and finally relevant contentpage.js:
<View style={styles.container}>
<SearchBox navigation={this.props.navigation} eventTypeId={this.state.eventTypeId} />
<Image source={imageType(this.state.dataSource[0].eventTypeId)} style={styles.eventType} />
<Text style={styles.textToDisplay}>
{this.state.dataSource[0].eventTypeDescription}
</Text>
</View>
On load, the box is prepopulated with "abcefg" as expected. Changing the contents hits the reducer as expected. However when I navigate to a content page which loads the search box again the value is empty, regardless if I've changed the original state or not.
Am I missusing redux for what it's intended? Should I be doing this a different way?
For clarity below is the organisation of the components
In the reducer(), you are accessing action.text but in the dispatch(), you passed postcode value to postcode instead of text.

React native navigation: '_this2.props.navigation.navigate'

my App.js code
import React from 'react'
import { Icon } from 'react-native-elements'
import { StyleSheet, View, StatusBar } from 'react-native'
import { createAppContainer } from 'react-navigation'
import { createStackNavigator } from 'react-navigation-stack'
import HomeScreen from './screens/homeScreen'
import LoginScreen from './screens/loginScreen'
import SignupScreen from './screens/signup'
import Screen2 from './screens/screen2'
import Screen1 from './screens/screen1'
export default class App extends React.Component {
setLogged(){
this.setState({logged:true})
this.forceUpdate()
}
//change to false when want to enable login feature else true
state = {
logged: false,
}
render() {
if(this.state.logged){
return(
<View style={styles.container} >
<StatusBar hidden = {false}/>
<AppContainer />
</View>
)
}else{
return(
<View style={styles.container} >
<StatusBar hidden = {false}/>
<LoginScreen signup={()=>this.props.navigation.navigate('Signup')} success={()=>this.setLogged()} />
</View>
)
}
}
}
const styles = StyleSheet.create({
container: {
position: 'relative',
width: '100%',
height: '100%',
backgroundColor: '#000000',
},
})
const HomeAppContainer = createStackNavigator(
{
Home: {screen: HomeScreen},
Signup: { screen: SignupScreen },
Screen1: { screen: Screen1 },
Screen2: { screen: Screen2 },
},
{initialRouteName: "Home"},
);
const AppContainer = createAppContainer(HomeAppContainer)
and the login screen contains
import React, { Component } from 'react';
import { StyleSheet, View, Text, TouchableOpacity, TextInput, ToastAndroid } from 'react-native';
import Colors from '../constants/colors'
import GLOBAL from '../constants/global'
export default class LoginScreen extends Component {
state = {
email: '',
password: '',
}
login() {
if (userid == 'admin') {
ToastAndroid.show('Invalid email/password', ToastAndroid.SHORT);
} else {
GLOBAL.userid = userid;
this.props.success()
}
})
}
render() {
return (
<View style={styles.MainContainer}>
<Text style={styles.text}>Email:</Text>
<View style={styles.inputView}>
<TextInput
style={styles.inputs}
autoFocus
returnKeyType="next"
keyboardType="email-address"
onChangeText={(email) => { this.setState({ email: email }) }}
/>
</View>
<Text style={styles.text}>Password:</Text>
<View style={styles.inputView}>
<TextInput
style={styles.inputs}
secureTextEntry
onChangeText={(password) => { this.setState({password:password}) }}
/>
</View>
<View style={styles.buttonGroup}>
<TouchableOpacity
style={styles.button}
onPress={() => { this.login() }} >
<Text style={{ fontSize: 24 }}>Sign in</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => { this.props.signup() }}>
<Text style={{ fontSize:24 }}>Sign up</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
//my styles here
});
the error is as follows
TypeError: undefined is not an object(evaluating '_this2.props.navigation.navigate')
i am learning react-native and making this login screen.
The app will check if the user is already logged in. if not then he will be prompted login screen which will have sign up option.
If the user is already logged in the app will directly go to home screen where screen1 and screen2 is used in stack
Well, the navigation prop is only passed down the navigation tree. Your App component is not a navigation screen like Home/Signup... (it doesn't have a route)... So basically App is not part of a navigator, it's not created using createStackNavigator or the others.
So, in conclusion the navigation prop is not defined.
Basically, how to fix this: LoginScreen should also be a route inside a navigator, therefore the navigation prop will be defined. Your App component should not render anything besides the <AppContainer /> and further logic should be handled in the first screen defined in the routes, which in your case might be the LoginScreen. In there you'll check if the user is logged in or not and navigate accordingly.
There's a great guide on their website on how to accomplish authentication flow: https://reactnavigation.org/docs/en/4.x/auth-flow.html

Can't navigate to other screen from the screen in TabNavigator

I trying to render StackNavigator inside TabNavigator. The Tabs are working fine, but i cannot link the button
In simple words, there's a button inside the "Feed" section of the TabNavigator "Tabs" . When clicked on the button, it should go to "UserDetails.js" via StackNavigator.
Please help!
Here is my index.android.js
export default class HackernoonNavigation extends Component {
render() {
return (
<Tabs />
);
}
}
export const Tabs = TabNavigator({
Feed: {
screen: Feed,
},
Me: {
screen: Me,
},
});
And here is the file "Feed.js" , inside which there is a button that leads to "UserDetail.js"
export default class Feed extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to Feed!
</Text>
<Button
onPress={() => this.props.navigation.navigate('Details')}
title="User Details"
/>
</View>
);
}
}
export const FeedStack = StackNavigator({
Details: { screen: UserDetail },
});
You can do it by defining tabnavigator inside stacknavigator as screen like in this way -
const StackNaviApp = StackNavigator({
UserDetails: { screen: UserDetails},
Feed: {
screen: TabNavigator({
Feed: { screen: Feed},
Me: { screen: Me}
}, {
tabBarOptions: {
showIcon: true,
lazy: true,
activeTintColor: '#7567B1',
labelStyle: {
fontSize: 16,
fontWeight: '600'
}
}
})
}});
export default StackNaviApp;
Taking Feed & Me inside TabNavigator & UserDetails inside StackNavigator directly . For more detail you can refer the source code from here -
http://androidseekho.com/others/reactnative/switching-between-stack-navigator-to-tab-navigator/
So I have a ListView on a Tab Screen and when I click on the List Item its navigating to a component called QR Code. So when I click a List Item the camera will be opened in a new window with StackNavigator.... I show you my code down below. Maybe it helps you to find your fault.
import React, {
Component,
} from 'react';
import {
// AppRegistry,
// Dimensions,
ListView,
StyleSheet,
Text,
TouchableOpacity,
TouchableHighlight,
View,
AsyncStorage
} from 'react-native';
import { SwipeListView, SwipeRow } from 'react-native-swipe-list-view';
import moment from 'moment';
import{createStackNavigator} from 'react-navigation';
import { Icon } from 'react-native-elements'
import QrCode from '../components/QrCode';
class SwipeList extends Component {
static navigationOptions = {
title: 'Scanner auswählen',
headerTintColor: 'white',
headerStyle: {backgroundColor: '#444444'},
headerTitleStyle: { color: 'white' },
};
constructor(props) {
super(props);
this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
scannerAvailable:false,
listType: 'FlatList',
listViewData: this.ds,
}
...
goToQrCodeScreen=(scanner)=>{
this.props.navigation.navigate('QrCode',{
scannerName: scanner.scannerName,
scannerValidityEnd: scanner.scannerValidityEnd,
scannerId: scanner.scannerId,
dataMacroId: scanner.dataMacroId,
hash:scanner.hash,
requestKey: scanner.requestKey,
})
}
...
render() {
return (
<View style={styles.container}>
<View>
<SwipeListView
dataSource={this.state.listViewData}
renderRow={ data => (
<View >
<TouchableHighlight
onPress={()=>this.goToQrCodeScreen(data)}
style={styles.rowFront}
underlayColor={'#AAA'}>
<Text> <Text style={[styles.listItemName,styles.textBold] } >{data.scannerName} </Text>
<Text style={styles.listItemValid}> gültig bis {moment(data.scannerValidityEnd).format('DD.MM.YYYY')}</Text>
</Text>
</TouchableHighlight>
</View>
)}
renderHiddenRow={ (data, secId, rowId, rowMap) => (
<View style={styles.rowBack}>
<TouchableOpacity style={[styles.backRightBtn, styles.backRightBtnRight]} onPress={ _ => this.deleteRow(data.scannerId,rowMap, `${secId}${rowId}`) }>
<Icon
name='trash'
type='font-awesome'
color='white'/>
<Text style={styles.backTextWhite}>
Löschen</Text>
</TouchableOpacity>
</View>
)}
rightOpenValue={-100}
enableEmptySections={true}
/>
</View>
</View>
);
}
}
const StackNavigation = createStackNavigator(
{
SwipeList:SwipeList ,
QrCode:QrCode
},
{
initialRouteName: 'SwipeList',
}
);
export default StackNavigation;
I deleted the code you dont need. I call the method goToQrCodeScreen() to navigate.
My fault was that I dont exported the StackNavigator. Now its working.
you need to wrap your feed in another component :
const FeedStack = createStackNavigator();
function FeedWrapper() {
return (
<HomeStack.Navigator>
<FeedStack .Screen name="Feed" component={Feed} />
<FeedStack .Screen name="Details" component={Details} />
</HomeStack.Navigator>
);
}
see https://reactnavigation.org/docs/tab-based-navigation

Navigate after login

I'm creating an app using React-Native. I added Firebase Auth to my application, but I can't navigate to the Homescreen after log in.
This is my code:
constructor (in LoginScreen):
constructor(props){
super(props)
this.state = {
email: '',
password: '',
status: '',
}
this.handlePress = this.handlePress.bind(this)
}
app.js:
import React from 'react';
import { AppRegistry } from 'react-native';
import { StackNavigator } from 'react-navigation';
import LoginScreen from './app/screens/LoginScreen';
import RegisterScreen from './app/screens/RegisterScreen';
import HomeScreen from './app/screens/HomeScreen';
const Stylelist = StackNavigator({
Login: { screen: LoginScreen },
Register: { screen: RegisterScreen},
Home: {screen: HomeScreen},
},{headerMode: "none"});
export default Stylelist;
handlePress function(function in the Loginscreen):
handlePress(){
firebaseRef.auth().signInWithEmailAndPassword(this.state.email, this.state.password).then(function(firebaseUser){
//Success, move to homepage.
console.log("logged in!")
navigate("Home")
}).catch(function(error){
//Failed to log in, print error.
});
}
This "logged in" does gets printed in the console but won't move to the Homescreen.
this is the render method in the loginScreen.js:
import React, { Component } from 'react';
import { View, StyleSheet, Text, TouchableOpacity, KeyboardAvoidingView, TextInput, StatusBar, Image } from 'react-native';
import { firebaseRef } from '../services/Firebase';
export default class LoginScreen extends Component{
constructor(props){
super(props)
this.state = {
email: '',
password: '',
status: '',
}
this.handlePress = this.handlePress.bind(this)
}
//Trying to login the account with email and password provided by the user.
handlePress(){
firebaseRef.auth().signInWithEmailAndPassword(this.state.email, this.state.password).then(function(firebaseUser){
//Success, move to homepage.
console.log("logged in!")
this.props.navigation.navigate("Home");
}).catch(function(error){
//Failed to log in, print error.
});
}
render(){
const { navigate } = this.props.navigation;
return(
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<StatusBar
barStyle="light-content"
/>
<View style={styles.logoContainer}>
<Image
style={styles.logo}
source={require('../assets/Logo.png')}/>
</View>
<View style={styles.formContainer}>
<TextInput
style={styles.txtInput}
keyboardType="email-address"
placeholder="email"
onChangeText={(text)=> this.setState({email: text})}
placeholderTextColor="#FFFFFF"
onSumbitEditing={()=>this.passwordInput.focus()}
returnKeyType="next"
autoCapitalize="none"
autoCorrect={false}
/>
<TextInput
style={styles.txtInput}
placeholder="password"
placeholderTextColor="#FFFFFF"
onChangeText={(text)=> this.setState({password: text})}
returnKeyType="go"
autoCapitalize="none"
autoCorrect={false}
secureTextEntry
ref={(input)=>this.passwordInput = input}
/>
<TouchableOpacity style={styles.logBtn} onPress={this.handlePress}>
<Text style={styles.logTxt}>
Login
</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.regBtn} onPress={()=>navigate("Register")}>
<Text style={styles.regTxt}>
create account
</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
}
I don't get any error printed and the app does not crash, what am I doing wrong?
I think you are missing the context inside the promise and navigate no longer exists there. So let's pass a parameter navigate callback to make it work:
Firstly, add navigate as a parameter to handlePress
handlePress(navigate){
firebaseRef.auth().signInWithEmailAndPassword(this.state.email, this.state.password).then(function(firebaseUser){
//Success, move to homepage.
console.log("logged in!")
navigate("Home");
}).catch(function(error){
//Failed to log in, print error.
});
}
Secondly, modify the TouchableOpacity to call handlePress properly:
...
<TouchableOpacity style={styles.logBtn} onPress={() => this.handlePress(navigate)}>
<Text style={styles.logTxt}>
Login
</Text>
</TouchableOpacity>
...
I know this was resolved but, no resolution was posted here. If anyone is having issues make sure you can access the navigation prop (try console logging it) another issue may be if you're conditionally rending your navigation stack based if auth exists, that will mean that one stack does not have access to the to other

importing a screen from the same JS file

I have consulted every question in StackOverflow regarding this problem with no success. I continuously get the following error `Route 'Home' should declare a screen.
This error seems to indicate that I must import a component into my working file if I want to use it as a screen, is that the case? If so, Why? That's probably what's wrong with my code, otherwise, I'm not sure what is wrong here: I would like to understand why this isn't working, I have consulted multiple guides on the subject.
my index.android.js:
import './app/index.js'
my index.js (not in full):
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View, Button, Image, TextInput }
from 'react-native';
import { StackNavigator } from 'react-navigation';
const RallyMobileNavigator = StackNavigator({
Home: { screen: RallyMobile },
LogIn: { screen: LogIn }
},{
initialRouteName: 'Home'
});
class RallyMobile extends Component {
static navigationOptions = {
title: 'Welcome',
};
state = {
initialPosition: {},
lastPosition: {},
userData: [],
}
render() {
return (
<View style={styles.container}>
<View style={styles.header}>
<Button
onPress={() => navigate('LogIn')}
title='Sign In'
/>
</View>
<View style={styles.EventListContainer}>
<EventList
location={this.state.lastPosition.location ?
this.state.lastPosition.location :
this.state.initialPosition.location}
/>
</View>
</View>
);
};
};
class LogIn extends Component {
state = {
userName: '',
password: ''
}
static navigationOptions = {
title: 'Sign In',
};
logInUser = () => {
console.log("test");
}
render() {
return(
<View>
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Username"
onChangeText={(text) => this.setState({userName})}
/>
<TextInput
style={{height: 40}}
placeholder="Password"
onChangeText={(text) => this.setState({password})}
/>
</View>
<View>
<button
onPress={logInUser()}
title='Sign In'
/>
</View>
</View>
)
}
}
AppRegistry.registerComponent('RallyMobile', () => RallyMobileNavigator);
Move
const RallyMobileNavigator = StackNavigator({
Home: { screen: RallyMobile },
LogIn: { screen: LogIn }
},{
initialRouteName: 'Home'
});
To just above
AppRegistry.registerComponent('RallyMobile', () => RallyMobileNavigator);
Credit:
https://github.com/react-community/react-navigation/issues/571#issuecomment-284207331