javascript Firebase Simple Authentication in React Native - javascript

I'm following a tutorial to make basic sign up/login functionalities for my react native app but am running into a couple issues. My code is as below:
App.js
import React, { Component } from 'react';
'use strict';
import {
AppRegistry,
Text,
StyleSheet,
ActivityIndicator,
View,
} from 'react-native';
import Navigator from 'react-native-deprecated-custom-components';
import Login from './Login';
import Account from './Account';
import * as firebase from 'firebase';
var firebaseConfig = {
apiKey: "MY KEY",
authDomain: "MY DOMAIN",
databaseURL: "MY URL",
storageBucket: "MY BUCKET",
};
const firebaseApp = firebase.initializeApp(firebaseConfig);
import styles from '../styles/mainstyle.js';
export default class App extends Component {
constructor(props){
super(props);
this.state = {
// the page is the screen we want to show the user, we will determine that
// based on what user the firebase apI returns to us.
page: null
};
}
componentWillMount(){
// We must asynchronously get the auth state, if we use currentUser here, it'll be null
const unsubscribe = firebaseApp.auth().onAuthStateChanged((user) => {
// If the user is logged in take them to the accounts screen
if (user != null) {
this.setState({page: Account});
return;
}
// otherwise have them login
this.setState({page: Login});
// unsubscribe this observer
unsubscribe();
});
}
render() {
if (this.state.page) {
return (
// Take the user to whatever page we set the state to.
// We will use a transition where the new page will slide in from the right.
<Navigator
initialRoute={{component: this.state.page}}
configureScene={() => {
return Navigator.SceneConfigs.FloatFromRight;
}}
renderScene={(route, navigator) => {
if(route.component){
// Pass the navigator the the page so it can navigate as well.
// Pass firebaseApp so it can make calls to firebase.
return React.createElement(route.component, { navigator, firebaseApp});
}
}} />
);
} else {
return (
// Our default loading view while waiting to hear back from firebase
<View style={styles.container}>
<View style={styles.body}>
<ActivityIndicator size="large" />
</View>
</View>
);
}
}
}
AppRegistry.registerComponent('App', () => App);
Login.js
'use strict';
import {
AppRegistry,
Text,
TextInput,
View,
TouchableHighlight,
ToolbarAndroid,
ActivityIndicator
} from 'react-native';
import React, {Component} from 'react';
import Signup from './Signup';
import Account from './Account';
import styles from '../styles/mainstyle.js';
import Navigator from 'react-native-deprecated-custom-components';
export default class Login extends Component {
constructor(props){
super(props);
// We have the same props as in our signup.js file and they serve the same purposes.
this.state = {
loading: false,
email: '',
password: ''
}
}
render() {
// The content of the screen should be inputs for a username, password and submit button.
// If we are loading then we display an ActivityIndicator.
const content = this.state.loading ? <ActivityIndicator size="large"/> :
<View>
<TextInput
style={styles.textInput}
onChangeText={(text) => this.setState({email: text})}
value={this.state.email}
placeholder={"Email Address"} />
<TextInput
style={styles.textInput}
onChangeText={(text) => this.setState({password: text})}
value={this.state.password}
secureTextEntry={true}
placeholder={"Password"} />
<TouchableHighlight onPress={this.login.bind(this)} style={styles.primaryButton}>
<Text style={styles.primaryButtonText}>Login</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.goToSignup.bind(this)} style={styles.transparentButton}>
<Text style={styles.transparentButtonText}>New here?</Text>
</TouchableHighlight>
</View>;
// A simple UI with a toolbar, and content below it.
return (
<View style={styles.container}>
<View style={styles.body}>
{content}
</View>
</View>
);
}
login(){
this.setState({
loading: true
});
// Log in and display an alert to tell the user what happened.
this.props.firebaseApp.auth().signInWithEmailAndPassword(this.state.email, this.state.password
).then((userData) =>
{
this.setState({
loading: false,
username:"Joe"
});
this.props.navigator.push({
component: Account
});
}
).catch((error) =>
{
this.setState({
loading: false
});
alert('Login Failed. Please try again' + error.message);
});
}
// Go to the signup page
goToSignup(){
this.props.navigator.push({
component: Signup
});
}
}
AppRegistry.registerComponent('Login', () => Login);
Account.js
'use strict';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image,
TouchableHighlight,
ToolbarAndroid
} from 'react-native';
import React, {Component} from 'react';
import Login from './Login';
import styles from '../styles/mainstyle.js';
import Navigator from 'react-native-deprecated-custom-components';
// Styles specific to the account page
const accountStyles = StyleSheet.create({
email_container: {
padding: 20
},
email_text: {
fontSize: 18
}
});
export default class Account extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
}
}
componentWillMount() {
// get the current user from firebase
const userData = this.props.firebaseApp.auth().currentUser;
this.setState({
user: userData,
loading: false
});
console.log(userData);
console.log(this.props.firebaseApp)
console.log(this.props.firebaseApp.auth())
var isNewUser = true;
var ref = this.props.firebaseApp;
ref.onAuthStateChanged(function(authData) {
if (authData && isNewUser) {
// save the user's profile into the database so we can list users,
// use them in Security and Firebase Rules, and show profiles
ref.child("users").child(authData.uid).set({
provider: authData.provider,
name: getName(authData)
});
}
});
// find a suitable name based on the meta info given by each provider
function getName(authData) {
switch(authData.provider) {
case 'password':
return authData.password.email.replace(/#.*/, '');
case 'twitter':
return authData.twitter.displayName;
case 'facebook':
return authData.facebook.displayName;
}
}
}
render() {
// If we are loading then we display the indicator, if the account is null and we are not loading
// Then we display nothing. If the account is not null then we display the account info.
const content = this.state.loading ? <ActivityIndicator size="large"/> :
this.state.user &&
<View style={styles.body}>
<View style={accountStyles.email_container}>
<Text style={accountStyles.email_text}>{this.state.user.email}</Text>
</View>
<TouchableHighlight onPress={this.logout.bind(this)} style={styles.primaryButton}>
<Text style={styles.primaryButtonText}>Logout</Text>
</TouchableHighlight>
<TouchableHighlight onPress={this.logout.bind(this)} style={styles.primaryButton}>
<Text style={styles.primaryButtonText}>Logout</Text>
</TouchableHighlight>
</View>
;
return (
<View style={styles.container}>
<View style={styles.body}>
{content}
</View>
</View>
);
}
logout() {
// logout, once that is complete, return the user to the login screen.
this.props.firebaseApp.auth().signOut().then(() => {
this.props.navigator.push({
component: Login
});
});
}
}
AppRegistry.registerComponent('Account', () => Account);
Signup.js
'use strict';
import {
AppRegistry,
View,
ToolbarAndroid,
ActivityIndicator
} from 'react-native';
import { Header,Title,Container, Content, List, ListItem, InputGroup, Input, Icon, Text, Picker, Button } from 'native-base';
import styles from '../styles/mainstyle.js';
import React, {Component} from 'react';
import Login from './Login';
export default class Signup extends Component {
constructor(props) {
super(props);
this.state = {
// used to display a progress indicator if waiting for a network response.
loading: false,
// entered credentials
email: '',
password: ''
}
}
// A method to passs the username and password to firebase and make a new user account
signup() {
this.setState({
// When waiting for the firebase server show the loading indicator.
loading: true
});
// Make a call to firebase to create a new user.
this.props.firebaseApp.auth().createUserWithEmailAndPassword(
this.state.email,
this.state.password).then(() => {
// then and catch are methods that we call on the Promise returned from
// createUserWithEmailAndPassword
alert('Your account was created!');
this.setState({
// Clear out the fields when the user logs in and hide the progress indicator.
email: '',
password: '',
loading: false
});
this.props.navigator.push({
component: Login
});
}).catch((error) => {
// Leave the fields filled when an error occurs and hide the progress indicator.
this.setState({
loading: false
});
alert("Account creation failed: " + error.message );
});
}
render() {
console.log('hello');
// The content of the screen should be inputs for a username, password and submit button.
// If we are loading then we display an ActivityIndicator.
const content = this.state.loading ? <ActivityIndicator size="large"/> :
<Content>
<List>
<ListItem>
<InputGroup>
<Icon name="person" style={{ color: '#0A69FE' }} />
<Input
onChangeText={(text) => this.setState({email: text})}
value={this.state.email}
placeholder={"Email Address"} />
</InputGroup>
</ListItem>
<ListItem>
<InputGroup>
<Icon name="unlock" style={{ color: '#0A69FE' }} />
<Input
onChangeText={(text) => this.setState({password: text})}
value={this.state.password}
secureTextEntry={true}
placeholder={"Password"} />
</InputGroup>
</ListItem>
</List>
<Button style={styles.primaryButton} onPress={this.signup.bind(this)}>
Signup
</Button>
<Button onPress={this.goToLogin.bind(this)} style={styles.primaryButton}>
Go to Login
</Button>
</Content>
;
// A simple UI with a toolbar, and content below it.
return (
<Container>
<Header>
<Title>Sign Up</Title>
</Header>
{content}
</Container>
)
}
goToLogin(){
this.props.navigator.push({
component: Login
});
}
}
AppRegistry.registerComponent('Signup', () => Signup);
I am getting the following errors:
Error#1:
"Warning: View.propTypes has been deprecated and will be removed in a future version of ReactNative. Use ViewPropTypes instead."
I've been looking through my code and cannot find any reference to View.protoTypes. I'm unsure what this error is referring to.
Error#2:
"Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object.
Check the render method of App.
in App
in RCTView (created by View)
in View (created by AppContainer)
in RCTView (created by View)
in View (created by AppContainer)
in AppContainer"
I've looked on this website for a solution for this and have seen people mention that the class export may have been done incorrectly, but looking at my own code I feel like I've done this correctly.
Error#3:
"Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Check the render method of App."
Same as above, am I exporting my class incorrectly?

the warning #1 could be related to a specific package which uses deprecated or old code (not fully supported by your React version).
I suppose that Issue #2 and #3 may be relative to how you import Navigator rather than how you export some class, try with:
import { Navigator } from 'react-native-deprecated-custom-components';

Related

Issue with loading a button based on the loggedin state

I am trying different ways to display a conditional button based on the athtentication state, but i keep getting into trouble. I have an app.js that defines the stacknavigator, which adds a button to the header giving the option to log out if authenticated.
I wrote a handleLogout function that should perform this.
import React from 'react';
import { Button, Image, View, Text } from 'react-native';
import firebase from 'react-native-firebase';
import Loading from './Loading';
import SignUp from './SignUp';
import Login from './Login';
import Main from './Main';
import {createAppContainer} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import { useNavigation } from '#react-navigation/native';
// eslint-disable-next-line no-undef
handleLogOut = () => {
const navigation = useNavigation();
firebase
.auth()
.signOut()
.then(() => this.props.navigation.navigate('Login'))
.catch(error => this.setState({errorMessage: error.message}));
};
const AppNavigator = createStackNavigator(
{
Loading: Loading,
SignUp: SignUp,
Login: Login,
Main: Main,
},
{
initialRouteName: 'Loading',
defaultNavigationOptions: {
headerLeft: null,
headerRight: () => {
let button = this.loggedIn? (
<Button
onPress={this.handleLogOut}
title="Log-out"
color="#fff"
/>
)
:
(
<Button
onPress={() => alert('Please log in')}
title="Log-in"
color="#fff"
/>
)
return button;
},
headerStyle: {
backgroundColor: '#c6f1e7',
},
headerTintColor: '#59616e',
headerTitleStyle: {
fontFamily: 'Raleway-Regular',
fontWeight: '400',
},
},
},
);
export default createAppContainer(AppNavigator);
App.js calls on loading.js where the value for loggedin is declared, based on the authentciation state and then loads either main.js or sign-up. in this case the main page is loaded, which means that someone is authenticated:
// Loading.js
import React from 'react';
import {View, ActivityIndicator, StyleSheet} from 'react-native';
import firebase from 'react-native-firebase';
export default class Loading extends React.Component {
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.setState({ loggedIn: true })
this.props.navigation.navigate(user ? 'Main' : 'SignUp');
} else {
this.setState({ loggedIn: false })
}
});
}
render() {
return (
<View style={styles.container}>
<ActivityIndicator size="large" />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#c6f1e7',
},
});
Now the page redirects to main and shows the welcome message, which indicates that the user is logged in, but the button in the header is saying 'log-in' as well, which means the button is not chosen well. I assume that this is because the loggedin value is not read and it automatically sets it on loggedin: false.
Here is the code for main.js
// Main.js
import React from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import firebase from 'react-native-firebase';
import { createAppContainer } from 'react-navigation';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import Kowops from './Kowops';
import Scan from './Scan';
import Wallet from './Wallet';
export class Main extends React.Component {
state = { currentUser: null }
componentDidMount() {
const { currentUser } = firebase.auth()
this.setState({ currentUser })
}
render() {
const { currentUser } = this.state
return (
<View style={styles.container}>
<Text>
Hidiho {currentUser && currentUser.email}!
</Text>
</View>
)
}
}
const bottomTabNavigator = createBottomTabNavigator(
{
Main: {screen: Main},
Kowops: {screen:Kowops},
Scan: {screen:Scan},
Wallet: {screen:Wallet},
},
{
//initialRouteName: 'Main',
tabBarOptions: {
initialRouteName: 'Main',
activeTintColor: '#59616e',
inactiveTintColor: '#a9a9a9',
style: {
backgroundColor: '#c6f1e7',
}
},
});
export default createAppContainer(bottomTabNavigator);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
})
So I need to figure out how to ensure that the value of isloggedin is read properly and the script loads the right button.
Does anyone have a clue?
Thanks in advance!!
Tim
The key here is that you can't use state across different components without passing them as props or through navigation params in this case. You can't use the useNavigation hook outside of a functional component so you should pass the navigation object around when you need it outside of a component (handleLogout is not a component).
Here are some alterations I would make, however I would suggest that you will need to make further changes based on the idea that you can use navigation params to pass information between screens. See more here https://reactnavigation.org/docs/en/params.html.
App.js
DefaultNavigationOptions can be a function which has a navigation prop, this is the navigation object you can use to get params or navigate in the context of the router.
remove that eslint exception because you don't need it, just properly declare the variable. Remove the "this" from you handleLogout function call because it is not a class attribute. Use navigation.getParam to get the isLoggedIn variable which you can pass in the navigate function call.
const handleLogout = navigation => {
firebase
.auth()
.signOut()
.then(() => navigation.navigate('Login'))
.catch(error => this.setState({errorMessage: error.message}));
}
...
defaultNavigationOptions: ({navigation}) => ({
headerRight: () => {
const isLoggedIn = navigation.getParam('isLoggedIn', false);
let button = isLoggedIn ? (
<Button
onPress={() => handleLogOut(navigation)}
title="Log-out"
color="#fff"
/>
) : ...
} ...
Now Loading.js
Here you need to add a navigation param to your navigate call which can then be used in the header
...
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.props.navigation.navigate('Main', {isLoggedIn: true});
} else {
this.props.navigation.navigate('SignUp', {isLoggedIn: false});
}
});
here is a modified version of your code in snack that you can see will get the logged in param and show the logout button https://snack.expo.io/#dannyhw/intrigued-graham-crackers
You will need to make further changes to fix other functionality because I only changed the minimum to show that you can use the navigation param.

React native issue with navigation"

so I am really having a hard time with this navigation thing. I am not an expert in Js or react native, but since the person working on this app isn't in the company anymore, I am asked to make some tweaks here and there until a new person comes in but I am struggling with this navigation thing.
so I am using this to go to my two factor identification
any idea what does this navigate do anyway? how can I set it value? do it need to passe it somehow?
full code
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {
View,
Text,
TouchableHighlight,
Image,
TextInput,
} from 'react-native'
import {requiredFields} from '../helpers/forms'
import BusyButton from '../Common/BusyButton'
import DismissKeyboard from '../Common/DismissKeyboard'
import {Colors} from '../styles'
import LoginStyles from './LogInStyles'
const loginStyles = LoginStyles.createStyles()
/* eslint-disable camelcase,space-before-function-paren */
export default class TwoFactor extends Component {
static propTypes = {
screenProps: PropTypes.object.isRequired,
navigation: PropTypes.object.isRequired,
}
state = {
verificationCode: '',
}
handleSubmit = () => {
try{
const {screenProps: {auth: {sendVerificationCode}}, navigation: {navigate}} = this.props
if (requiredFields(['verificationCode'], ['Verification Code'], this.state)) {
sendVerificationCode(this.state.verificationCode,this.state.user, navigate)
}
return false
}catch(e){
console.log(e)
}
}
render() {
const {screenProps: {auth: {isFetching}}} = this.props
return (
<DismissKeyboard style={loginStyles.pageWrapper}>
<View style={loginStyles.logoContainer}>
<Image
style={loginStyles.logo}
resizeMode="contain"
source={require('../../assets/images/logo-IH_blue_gold-small.png')}
/>
</View>
<View style={loginStyles.containerExpand}>
<Text style={loginStyles.h1}>ID Verification</Text>
<Text style={loginStyles.label}>
A verification code has been sent to the phone number on file. Please enter the code below.
</Text>
<TextInput
style={loginStyles.input}
placeholder="Verification Code"
placeholderTextColor={Colors.lightGray}
onChangeText={(verificationCode) => this.setState({verificationCode})}
keyboardType="number-pad"
/>
</View>
<View style={loginStyles.container}>
<View style={loginStyles.buttonContainer}>
<BusyButton
style={loginStyles.button}
underlayColor={Colors.buttonPrimaryBkgd}
isBusy={isFetching}
onPress={this.handleSubmit}
>
<Text style={loginStyles.buttonText}>Next</Text>
</BusyButton>
</View>
</View>
</DismissKeyboard>
)
}
}
ERROR
it's not spitting any error, but it is not taking me to the next screen.

How to use react navigation outside of a screen

I would like to make my app move to the next page when a code is entered correctly, but I have been having much trouble doing so. I am working in a file names AccessForm.js , which is not a screen but is a component that is included in the access code screen. I tried using this.props.navigation.navigate('CreateAccountScreen');, but ran into the error "Undefined is not an object (evaluating 'this.props.navigation'). With some trial and error, I found out that I can only use react-navigation inside of an actual screen for some weird reason. After this, I made an attempt to use this.state and this.setState({})to keep track of a screen variable, and sync it to the actual access code screen, so i could use navigation. Unfortunately, this.setState also throws a "Undefined is not an object" error. I have pasted an abbreviated version of my code below. What would be the best way to achieve this navigating outside of a screen file issue?
App.js ---->
import { createStackNavigator, createAppContainer } from 'react-navigation';
import AccessScreen from './src/screens/AccessScreen';
import CreateAccountScreen from './src/screens/CreateAccountScreen';
const RootStack = createStackNavigator ({
EnterAccessCode : {
screen: AccessScreen
},
CreateAccount : {
screen: CreateAccountScreen
}
},
{
headerMode: 'none'
});
const App = createAppContainer(RootStack);
export default App;
AccessForm.js ---->
import React from 'react';
import { StyleSheet, Text, View, TextInput, AlertIOS } from 'react-native';
var firebase = require("firebase");
if (!firebase.apps.length) { // Don't open more than one firebase session
firebase.initializeApp({ // Initialize firebase connection
apiKey: "key",
authDomain: "domain",
databaseURL: "url",
storageBucket: "storage_bucket",
});
}
this.codesRef = firebase.database().ref('codes'); // A reference to the codes section in the db
// this.state = {
// screen: 0
// };
export default class LoginForm extends React.Component {
constructor(props) {
super(props);
//this.checkCode = this.checkCode.bind(this); // throws error
}
render() {
return (
<View style={styles.container} >
<TextInput
style={styles.input}
placeholder='Access Code'
returnKeyType='go'
onSubmitEditing={(text) => checkCode(text.nativeEvent.text)} // Checks the code entered
autoCapitalize='none'
autoCorrect={false}
/>
</View>
);
}
}
function checkCode(text) {
var code = text; // Set entered code to the var "code"
var identifier = ""; // Used to store unique code object identifier
codesRef.once('value', function(db_snapshot) {
let codeIsFound = false
db_snapshot.forEach(function(code_snapshot) { // Cycle through available codes in db
if (code == code_snapshot.val().value) { // Compare code to db code
codeIsFound = true;
identifier = code_snapshot.key; // Code object ID
}
})
if (codeIsFound) {
deleteCode(identifier); // Delete the code if used, maybe do this after account is created?
this.props.navigation.navigate('CreateAccountScreen');
//this.setState({screen: 1}); // this throws error
// MOVE TO NEXT SCREEN
//this.props.navigation.navigate('AccountCreateScreen'); // throws error
} else { // wrong code
// note to self : add error message based on state var
AlertIOS.alert("We're Sorry...", "The code you entered was not found in the database! Please contact Mr. Gibson for further assistance.");
}
});
}
function deleteCode(id) { // delete a code from unique ID
firebase.database().ref('codes/' + id).remove();
}
// stylesheet is below
Login.js ---->
import React from 'react';
import { StyleSheet, Text, View, Image, TextInput, KeyboardAvoidingView, Platform } from 'react-native';
import AccessForm from './AccessForm';
export default class App extends React.Component {
render() {
return (
<View>
<View style={styles.logoContainer}>
<Image
source={require('../images/mhs.jpg')}
style={styles.logo}
/>
<Text style={styles.app_title}>MHS-Protect</Text>
<Text>An app to keep MHS safe and in-touch.</Text>
</View>
<KeyboardAvoidingView style={styles.container} behavior='padding'>
<View style ={styles.formContainer}>
<AccessForm/>
</View>
</KeyboardAvoidingView>
</View>
);
}
}
//styles below
import React from 'react';
import { StyleSheet, Text, View, TextInput, AlertIOS } from 'react-native';
var firebase = require('firebase');
if (!firebase.apps.length) {
// Don't open more than one firebase session
firebase.initializeApp({
// Initialize firebase connection
apiKey: 'key',
authDomain: 'domain',
databaseURL: 'url',
storageBucket: 'storage_bucket',
});
}
export default class LoginForm extends React.Component {
constructor(props) {
super(props);
this.codesRef = firebase.database().ref('codes'); // A reference to the codes section in the db
}
checkCode = text => {
var code = text; // Set entered code to the var "code"
var identifier = ''; // Used to store unique code object identifier
this.codesRef.once('value', function(db_snapshot) {
let codeIsFound = false;
db_snapshot.forEach(function(code_snapshot) {
// Cycle through available codes in db
if (code == code_snapshot.val().value) {
// Compare code to db code
codeIsFound = true;
identifier = code_snapshot.key; // Code object ID
}
});
if (codeIsFound) {
this.deleteCode(identifier); // Delete the code if used, maybe do this after account is created?
this.props.navigation.navigate('CreateAccount');
} else {
// wrong code
// note to self : add error message based on state var
AlertIOS.alert(
"We're Sorry...",
'The code you entered was not found in the database! Please contact Mr. Gibson for further assistance.'
);
}
});
};
deleteCode = id => {
firebase
.database()
.ref('codes/' + id)
.remove();
};
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="Access Code"
returnKeyType="go"
onSubmitEditing={text => this.checkCode(text.nativeEvent.text)} // Checks the code entered
autoCapitalize="none"
autoCorrect={false}
/>
</View>
);
}
}
You should have navigation object in your props. By default, react navigation will pass navigation to all screens but other components. To do this, you have two options:
1. Pass navigation props from your screen to every child components (not recommended).
2. Use withNavigation as mention in document here https://reactnavigation.org/docs/en/connecting-navigation-prop.html
import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';
class MyBackButton extends React.Component {
render() {
return <Button title="Back" onPress={() => { this.props.navigation.goBack() }} />;
}
}
// withNavigation returns a component that wraps MyBackButton and passes in the
// navigation prop
export default withNavigation(MyBackButton);
Edit:
The checkCode method does not belong to your LoginForm. You need to:
1. Make it part of LoginForm .
2. Remember to use bind or arrow function definition. Otherwise, your this inside function is not defined.
import { withNavigation } from 'react-navigation';
class LoginForm extends React.Component {
checkCode = (text) => {
....
};
}
export default withNavigation(LoginForm);
You can read more about bind or arrow method here https://medium.com/shoutem/react-to-bind-or-not-to-bind-7bf58327e22a
Copy and paste(Reference) from: https://github.com/react-navigation/react-navigation/issues/1439#issuecomment-303661539
It works from me.
you can pass your top-level navigator ref to a service, and dispatch actions from that service.
// App.js
import NavigatorService from './services/navigator';
const Navigator = StackNavigator({ /* ... */ })
class App extends Component {
// ...
render(): {
return (
<Navigator
ref={navigatorRef => {
NavigatorService.setContainer(navigatorRef);
}}
/>
);
}
}
// services/navigator.js
// #flow
import { NavigationActions } from 'react-navigation';
import type { NavigationParams, NavigationRoute } from 'react-navigation';
let _container; // eslint-disable-line
function setContainer(container: Object) {
_container = container;
}
function reset(routeName: string, params?: NavigationParams) {
_container.dispatch(
NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName,
params,
}),
],
}),
);
}
function navigate(routeName: string, params?: NavigationParams) {
_container.dispatch(
NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName,
params,
}),
);
}
function navigateDeep(actions: { routeName: string, params?: NavigationParams }[]) {
_container.dispatch(
actions.reduceRight(
(prevAction, action): any =>
NavigationActions.navigate({
type: 'Navigation/NAVIGATE',
routeName: action.routeName,
params: action.params,
action: prevAction,
}),
undefined,
),
);
}
function getCurrentRoute(): NavigationRoute | null {
if (!_container || !_container.state.nav) {
return null;
}
return _container.state.nav.routes[_container.state.nav.index] || null;
}
export default {
setContainer,
navigateDeep,
navigate,
reset,
getCurrentRoute,
};
and then you can use Navigator service everywhere.
Like:
import NavigatorService from './services/navigator';
NavigatorService.navigate('Home');

undefined is not an object error in react native

I'm trying to create a route to a screen by pressing a button in a side menu using react navigation. When I try to implement it, I get the following error:
undefined is not an object (evaluating '_this.props.user.Range')
The error flags, point something wrong in here:
RangeValues: this.props.user.Range
The rest of the components I am trying to navigate to are here:
import React, {Component} from 'react'
import {
StyleSheet,
View,
Text,
Switch,
} from 'react-native'
import Slider from 'react-native-multislider'
export default class Profile extends Component {
state = {
RangeValues: this.props.user.Range,
distanceValue: [this.props.user.distance]
}
render() {
const {name, work, id} = this.props.user
const {RangeValues, distanceValue} = this.state
return (
<View style={styles.container}>
<View style={styles.profile}>
<Text style={{fontSize:20}}>{name}</Text>
</View>
<View style={styles.label}>
<Text>Distance</Text>
<Text style={{color:'darkgrey'}}>{distanceValue}km</Text>
</View>
<Slider
min={1}
max={100}
values={distanceValue}
onValuesChange={val => this.setState({distanceValue:val})}
onValuesChangeFinish={val => this.updateUser('distance', val[0])}
/>
<View style={styles.label}>
<Text>Age Range</Text>
<Text style={{color:'darkgrey'}}>{ageRangeValues.join('-')}</Text>
</View>
<Slider
min={1}
max={200}
values={RangeValues}
onValuesChange={val => this.setState({RangeValues:val})}
onValuesChangeFinish={val => this.updateUser('Range', val)}
/>
</View>
)
}
}
I want to navigate from a button in a drawer using stack navigator, my routing is as follows:
import { StackNavigator } from 'react-navigation';
import React from 'react';
import Home from './screens/home';
import Login from './screens/login';
import Profile from './screens/profile';
const RouteConfigs = {
Login: { screen: Login },
Home: { screen: Home },
Profile: { screen: Profile },
};
const StackNavigatorConfig = {
headerMode: 'none',
}
export default StackNavigator(RouteConfigs, StackNavigatorConfig)
Where I am trying to navigate from is the Home screen component. The function I have used for this within the Home component is:
() => this.props.navigation.navigate('Profile', { user: this.state.user })
Does anybody know how to fix this error in order for me to navigate from the home component to the profile component?
I think you should bind your props from constructor :
export class MyClass extends Component {
constructor(props) {
super(props);
if (!this.state) {
this.state = {
data: props.myData
}
}
render() {DO THE JOB HERE}
}
}
Then you can access to this.state instead of state
For more explanations, props are not yet available since the constructor has not pass. See Component doc : https://facebook.github.io/react/docs/react-component.html#constructor

React Native Element type is invalid expected a string but got undefined

I'm following a course on react-native and I just ran into this error when trying to add a modal component to a page:
Element type is invalid: expected a string(for built-in components) or a class/function (for composite components) but got: undefined.
Here's my code:
EmployeeEdit.js
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Communications from 'react-native-communications';
import { employeeUpdate, employeeSave, employeeReset } from '../actions';
import { Card, CardSection, Button, Confirm } from './common';
import EmployeeForm from './EmployeeForm';
class EmployeeEdit extends Component {
constructor(props) {
super(props);
this.state = { showModal : false};
this.onButtonPress = this.onButtonPress.bind(this);
this.onTextPress = this.onTextPress.bind(this);
}
componentWillMount() {
_.each(this.props.employee, (value, prop) => {
this.props.employeeUpdate({prop, value});
});
}
componentWillUnmount() {
this.props.employeeReset();
}
onButtonPress(){
const { name, phone, shift } = this.props;
this.props.employeeSave({ name, phone, shift, uid: this.props.employee.uid });
}
onTextPress(){
const { name, phone, shift } = this.props;
Communications.text(phone, `Hello ${name}, your upcoming shift is on ${shift}`);
}
render () {
return (
<Card>
<EmployeeForm {...this.props} />
<CardSection>
<Button onPress={this.onButtonPress}>
Save Changes
</Button>
</CardSection>
<CardSection>
<Button onPress={this.onTextPress}>
Text Schedule
</Button>
</CardSection>
<CardSection>
<Button onPress={()=> this.setState({ showModal: !this.state.showModal })}>
Fire Employee
</Button>
</CardSection>
<Confirm
visible={this.state.showModal}
>
Are you sure you want to fire this employe?
</Confirm>
</Card>
);
}
}
const mapStateToProps = (state) => {
const {name, phone, shift} = state.employeeForm;
return {name, phone, shift};
};
export default connect(mapStateToProps, {
employeeUpdate,
employeeSave,
employeeReset
})(EmployeeEdit);
Confirm.js
import React from 'react';
import { Text, View, Modal } from 'react-native';
import { CardSection } from './CardSection';
import { Button } from './Button';
const Confirm = ({ children, visible, onAccept, onDecline }) => {
const { containerStyle, textStyle, cardSectionStyle } = styles;
return (
<Modal
visible={visible}
transparent
animationType='slide'
onRequestClose={() => {}}
>
<View style={containerStyle}>
<CardSection style={cardSectionStyle}>
<Text style={textStyle}>
{children}
</Text>
</CardSection>
<CardSection>
<Button onPress={onAccept}>Yes</Button>
<Button onPress={onDecline}>No</Button>
</CardSection>
</View>
</Modal>
);
};
const styles = {
cardSectionStyle: {
justifyContent: 'center'
},
textStyle: {
flex: 1,
fontSize: 18,
textAlign: 'center',
lineHeight: 40
},
containerStyle: {
backgroundColor: 'rgba(0, 0, 0, 0.75)',
position: 'relative',
flex: 1,
justifyContent: 'center'
}
};
export default { Confirm };
The error only occurs when I add the confirm component to EmployeeEdit. When I remove the confirm component at the bottom the error goes away. Is there some error in my confirm component?
Thanks
at the bottom of Confirm.js it should be
export { Confirm };
not
export default { Confirm };
When you're importing Confirm using curly braces
import { Card, CardSection, Button, Confirm } from './common';
make sure you're not exporting it using default like what you did:
export default { Confirm };
because once you use
default syntax, it doesn't require curly braces syntax when importing.
You can avoid the flower braces in all.
Do this.
In your Confirm.js
export default Confirm
In your EmployeeEdit.js
import Confirm from './Confirm'
As you see, I have omitted the braces. According to the ES6 Documentation, if a module defines a default export, then you can import the default export without the use of curly braces. Refer here: What is "export default" in javascript?

Categories

Resources