undefined is not an object error in react native - javascript

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

Related

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.

Adding an event handler on a createBottomTabNavigator of React Native

I have a React Native app which main navigation is accomplished using a
createBottomTabNavigator
as follows:
import React from 'react';
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';
import Icon from 'react-native-vector-icons/Ionicons'
import HomeStack from './HomeStack'
import SettingsStack from './SettingsStack'
const TabNavigator = createBottomTabNavigator({
Home: {
screen: HomeStack,
navigationOptions: {
tabBarIcon:({tintColor})=>(
<Icon name="ios-home" color={tintColor} size={24}/>
)
}
},
Settings: {
screen: SettingsStack,
navigationOptions: {
tabBarIcon:({tintColor})=>(
<Icon name="ios-settings" color={tintColor} size={24}/>
)
}
}
});
export default createAppContainer(TabNavigator)
As you can see, it basically includes two other components that are stack navigators themselves. I will not include them in order to have cleaner question. What I've added to my app is real-time push notifications as described here. Everything seems to be working fine until now but I have added the notification handler:
_handleNotification = (notification) => {
this.setState({notification: notification});
};
in my HomeScreen which is part of my HomeStack (The first screen). I don't really like this structure (having the handler in my HomeScreen). I have another screen (in the same HomeStack) that I'd like to handle this new coming notification. But I assume it is not gonna happen until this other screen is not mounted. So, I was wondering, is it possible to somehow define the handler on the level of the main TabNavigator and when handled just to redirect to my dedicated screen? I assume this is a bit cleaner approach.
This is easy to implement if you just create a custom Tab Bar component.
TabBar config
import CustomTabBar from '???';
const TabRoutes = createBottomTabNavigator({
Home: {screen: HomeStack},
Settings: {screen: SettingsRoutes}
},{
tabBarComponent: CustomTabBar,
});
export default createAppContainer(TabNavigator)
CustomTabBar
import CustomTabBarItem from '???';
class CustomTabBar extends React.Component {
public constructor(props: Props) {
super(props);
this.state = {
notification: undefined
};
}
render() {
const {navigation} = this.props;
const routes = navigation.state.routes;
return (
<View>
{routes.map(route => {
// Just some basic example, create a CustomTabBarItem according your own needs
<CustomTabBarItem
key={route.key}
onPress={() => navigation.navigate(route.routeName)}
routeName={route.routeName}
/>
})}
</View>
);
}
_handleNotification = (notification) => {
this.setState({notification: notification});
};
}

Can't find variable error in react-native even if I am not using it

So I am learning React-Native and I have created a Drawer which has already worked before. But after a few modifications not involving the Drawer it keeps giving this error Can't find variable: Contact even if I am not importing a variable called Contact.
drawer.js
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { DrawerNavigator, DrawerItems } from 'react-navigation';
import ButtonScreen from '../screens/ButtonScreen';
import Contacts from '../screens/Contacts';
import Information from '../screens/Information';
import Preferences from '../screens/Preferences';
const Drawer = new DrawerNavigator({
Home: {
screen: ButtonScreen,
navigationOptions: {
drawerLabel: 'Button'
},
},
Contacts: {
screen: Contacts,
},
Preferences: {
screen: Preferences,
},
Info: {
screen: Information,
}
}, {
contentComponent: (props) => (
<View>
<Text>Custom Header</Text>
<DrawerItems {...props}/>
<Text>Custom Footer</Text>
</View>
)
});
export default Drawer;
Contacts.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { View, Text } from 'react-native';
class Contacts extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View>
<Text>
Contact Component
</Text>
</View>
);
}
}
const mapStateToProps = (state) => {
const { isDrawerOpened } = state;
return { isDrawerOpened };
}
export default connect(mapStateToProps)(Contacts);
By the stacktrace the error is comming from drawer.js line 6, where I import Contacts and not Contact. I already runned npm start -- --reset-cache to see if it would solve it but no. I am very confuse about why this is happening.

Why am I getting an invalid value error?

I don't understand how I'm getting this error (pic below). In my LoginForm.js file, the onEmailChange(text) is giving me an unresolved function or method call to onEmailChange() error when I hover over it in my WebStorm IDE. In my index.js file, no error is being thrown anywhere.
I've looked around SO for this issue but it doesn't fully pertain to my problem.
I've tried File > Invalidate Caches/Restart but that didn't work.
Here's App.js:
import React, { Component } from 'react';
import {StyleSheet} from 'react-native';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import firebase from 'firebase';
import reducers from './reducers';
import LoginForm from './components/common/LoginForm';
class App extends Component {
render() {
return(
<Provider style={styles.c} store={createStore(reducers)}>
<LoginForm/>
</Provider>
);
}
}
const styles = StyleSheet.create({
c: {
flex: 1
}
});
export default App;
Here's LoginForm.js:
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {emailChanged} from 'TorusTeensApp/src/actions';
import {Text, StyleSheet, KeyboardAvoidingView, TextInput, TouchableOpacity} from 'react-native';
class LoginForm extends Component {
render() {
onEmailChange(text)
{
this.props.emailChanged(text);
}
return(
<KeyboardAvoidingView style={styles.container}>
<TextInput
style={styles.userInput}
onsubmitediting={() => this.passwordInput.focus()}
returnKeyType={"next"}
placeholder={"Email"}
label={"Email"}
keyboardType={"email-address"}
autoCorrect={false}
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
/>
<TextInput
style={styles.userInput}
ref={(userInput) => this.passwordInput = userInput}
returnKeyType={"go"}
placeholder={"Password"}
label={"Password"}
secureTextEntry
/>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.buttonText}>Login</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.buttonText}>Create Account</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
);
}
}
const styles = StyleSheet.create({
container: {
padding: 20 // creates a gap from the bottom
},
userInput: {
marginBottom: 20,
backgroundColor: '#9b42f4',
height: 40
},
buttonContainer: {
backgroundColor: '#41bbf4',
paddingVertical: 10,
marginBottom: 20
},
buttonText: {
textAlign: 'center',
color: '#FFFFFF'
}
});
const mapStateToProps = state => {
return {
email: state.auth.email
};
};
export default connect(mapStateToProps, null, {emailChanged}) (LoginForm);
Here's index.js:
import {EMAIL_CHANGED} from './types';
export const emailChanged = (text) => {
return {
type: 'EMAIL_CHANGED',
payload: text
};
};
export default emailChanged();
Your connect is miswired
connect(mapStateToProps, null, {emailChanged}) (LoginForm);
It should be something like:
connect(mapStateToProps,
(dispatch) => ({emailChanged: (text) => dispatch(emailChanged(text))})
)(LoginForm);
so that your action actually gets dispatched
and as spotted by emed in comment:
export default emailChanged;
without parentheses.
You defined your callback inside your render() method and not inside the class body. Do it like this:
class LoginForm extends Component {
onEmailChange(text) {
this.props.emailChanged(text);
}
render() {
return(...);
}
}
Also you shouldn't bind methods inside your render() method. Do it in the constructor of your Component:
class LoginForm extends Component {
constructor(props) {
super(props);
this.onEmailChange.bind(this);
}
onEmailChange(text) {
// do something
}
// other methods
}
Or if you use babel and ES6, you can define your callback with an arrow function, then it will be automatically bound:
class LoginForm extends Component {
onEmailChange = text => {
// do something
};
// other methods
}
See also the react docs about autobinding.
Also your call to connect seems incorrect. If you want to dispatch the action emailChanged it has to look like this:
const mapStateToProps = state => {
return {
email: state.auth.email
};
};
const mapDispatchToProps = dispatch => {
// this put a function emailChanged into your props that will dispatch the correct action
emailChanged: text => dispatch(emailChanged(text))
};
const LoginFormContainer = connect(mapStateToProps, mapDispatchToProps)(LoginForm);
export default LoginFormContainer;
The third argument to connect needs to be a function that knows how to merge the output of mapStateToProps, mapDispatchToProps, and ownProps all into one object that is then used as props for your connected component. I think you're trying to pass that action to the mapDispatchToProps argument, which is the second argument not the third. So, based on what I think you're doing, you probably wanna change your connect line to look like this.
export default connect(mapStateToProps, {emailChanged}) (LoginForm);
Then, export the function from your actions file not the output of calling that function.
export default emailChanged;
Notice I removed the parentheses so it's not being called.
Then make the callback function a method on your class and bind it in the constructor.
constuctor(props) {
super(props);
this.onEmailChange = this.onEmailChange.bind(this);
}
onEmailChange(text) {
this.props.emailChanged(text);
}
Then update onChangeText on that element.
onChangeText={this.onEmailChange}

javascript Firebase Simple Authentication in React Native

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';

Categories

Resources