Converting class based component to function based component fails - javascript

I am switching from class-based component to function-based components, but my code works in class-based component and doesn't work in the function-based component.
Here is the code:
import { connect } from "react-redux";
import * as actions from "../redux/actions/authActions";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
};
}
authenticate() {
this.props.auth(this.state.email, this.state.password).then((response) => {
if (this.props.authenticated) {
alert("User Is Authenticated");
} else {
alert("User Isn't Authenticated");
}
});
}
render() {
return (
<View style={{ flex: 1 }}>
<TextInput
autoCapitalize="none"
keyboardType="email-address"
style={// styles here}
placeholder="Enter email"
value={this.state.email}
onChangeText={(email) => this.setState({ email })}
/>
<TextInput
autoCapitalize="none"
secureTextEntry
style={// styles here}
placeholder="Enter password"
value={this.state.password}
onChangeText={(password) => this.setState({ password })}
/>
<TouchableOpacity onPress={() => this.authenticate()}>
<Text style={{ marginTop: 20, color: "black", textAlign: "center" }}>
Login
</Text>
</TouchableOpacity>
</View>
);
}
}
const mapStateToProps = (state) => ({
isLoggedIn: state.auth.isLoggedIn,
isLoading: state.auth.isLoading,
userData: state.auth.userData,
error: state.auth.error,
authenticated: state.auth.isAuthenticated,
mainState: state,
});
const mapDispatchToProps = (dispatch) => ({
auth: (email, password) => dispatch(actions.loginUser({ email, password })),
});
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Converted code to function based component
import { connect } from "react-redux";
import * as actions from "../redux/actions/authActions";
function Login({ auth, authenticated }) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const authenticate = () => {
auth(email, password).then((response) => {
if (authenticated) {
alert("User Is Authenticated");
} else {
alert("User Isn't Authenticated");
}
});
};
return (
<View style={{ flex: 1 }}>
<TextInput
autoCapitalize="none"
keyboardType="email-address"
style={ // styles here}
placeholder="Enter email"
value={email}
onChangeText={(text) => setEmail(text)}
/>
<TextInput
autoCapitalize="none"
secureTextEntry
style={ // styles here}
placeholder="Enter password"
value={password}
onChangeText={(text) => setPassword(text)}
/>
<TouchableOpacity onPress={() => authenticate()}>
<Text style={{ marginTop: 20, color: "black", textAlign: "center" }}>
Login
</Text>
</TouchableOpacity>
</View>
);
}
const mapStateToProps = (state) => ({
isLoggedIn: state.auth.isLoggedIn,
isLoading: state.auth.isLoading,
userData: state.auth.userData,
error: state.auth.error,
authenticated: state.auth.isAuthenticated,
mainState: state,
});
const mapDispatchToProps = (dispatch) => ({
auth: (email, password) => dispatch(actions.loginUser({ email, password })),
});
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Here at the Login function. App state isn't updated at the first time, but it gets updated in subsequent attempts.
Thank you for reading and helping.

You've closed over stale state from the render cycle that authenticate was invoked in. React state updates are asynchronous, so if you want to handle state after an update you need to do so in the next render cycle, likely in an useEffect hook (synonymous to the class-based component's componentDidUpdate method). When the authenticated redux state value updates then the component is rerendered.
useEffect(() => {
if (authenticated) {
alert("User Is Authenticated");
} else {
alert("User Isn't Authenticated");
}
}, [authenticated]);
authenticate() {
auth(email, password)
.then((response) => {
console.log(response);
});
}
Update
This useEffect callback will display one alert or the other, each render. You can add state to make it wait until you've submitted an auth request AND have been authenticated.
const [isAuthenticating, setIsAuthenticating] = useState(false);
const [finishedAuth, setFinishedAuth] = useState(false);
useEffect(() => {
if (isAuthenticating && finishedAuth) {
alert(authenticated
? "User Is Authenticated"
: "User Isn't Authenticated"
);
}
}, [isAuthenticating, finishedAuth, authenticated]);
authenticate() {
setIsAuthenticating(true);
auth(email, password)
.then((response) => {
console.log(response);
})
.finally(() => setFinishedAuth(true));
}
These two additional state might be great candidates to instead store in your redux state, BTW.

Related

React Native Functional Components - undefined is not an object (evaluating 'navigation.navigate')

I am practicing moving from class components to functional components in react native and I am having trouble understanding how to access navigation from a component. Normally I would run something like this.props.navigation.navigate('screen'). Now I am normally passing navigation as a prop.
But it doesn't seem to work in the following example. Where am I going wrong? I am using react-navigation:
import React, { useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Container, Form, Input, Item, Button, Label } from "native-base";
import * as firebase from "firebase";
const LoginForm = ({navigation}) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const addEmail = (email) => {
setEmail(email)
}
const addPassword = (password) => {
setPassword(password)
}
return (
<Form>
<Item floatingLabel>
<Label>Email</Label>
<Input
autoCorrect={false}
autoCapitalize="none"
onChangeText={(email)=>addEmail(email)}
/>
</Item>
<Item floatingLabel>
<Label>Password</Label>
<Input
secureTextEntry={true}
autoCorrect={false}
autoCapitalize="none"
onChangeText={(password)=>addPassword(password)}
/>
</Item>
<Button style={{ margin: 10 }}
full
rounded
success
onPress={()=>handleLogin(email, password, navigation)}
>
<Text style={{ color: 'white' }}>Login</Text>
</Button>
</Form>
);
};
export default LoginForm;
const handleLogin = (email, password, navigation) => {
console.log(email, password)
firebase.auth()
.signInWithEmailAndPassword(email.trim(), password)
.then(() => {firebase.auth().currentUser.emailVerified ? navigation.navigate('Home') : navigation.navigate('StartScreen')})
.catch(error => console.log(error))
}
I create a stack navigator in App.js
const bottomTabNavigator = createBottomTabNavigator(
{
Home: {
screen: Home,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Ionicons name="ios-home" size={25} color={tintColor}/>
// <Icon name="qrcode" size={25} color={tintColor} />
)
}
},
Profile: {
screen: Profile,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
// <Icon name="search" size={25} color={tintColor} />
<Ionicons name="md-person" size={25} color={tintColor}/>
)
}
},
},
{
initialRouteName: 'Home',
tabBarOptions: {
activeTintColor: '#eb6e3d'
}
}
);
const RootSwitch = createSwitchNavigator({
StartScreen,
Signup,
Login,
bottomTabNavigator
});
const AppContainer = createAppContainer(RootSwitch);
try using hooks, like this:
//add this import
import React, { useCallback } from 'react';
//change function to callback
const handleLogin = useCallback(async(email,password,navigation) => {
console.log(email, password)
await firebase.auth()
.signInWithEmailAndPassword(email.trim(), password)
.then(() => {firebase.auth().currentUser.emailVerified ? navigation.navigate('Home') : navigation.navigate('StartScreen')})
.catch(error => console.log(error))
}, []);

Fix "Cannot update component from inside function body of different component" warning in React Native app using Context

I have a simple RN app that pretty much only has login so far. It uses Apollo GraphQL to call the backend and stores the user token and name in a React Context object. The context object has the user and a setUser function (and is instantiated in App.js). The login screen calls setUser from the UserContext object on a successful login.
This has been working fine but today I updated to React 16.3 and started getting the warning:
Cannot update a component from inside the function body of a different component
And lists the line where I am calling the setUser function if we received a succesful response.
The App.js uses useState to track the user and setUser function and puts that in a object to pass into the UserContext.Provider:
export default function App(props) {
const [user, setUser] = useState(null)
const contextVal = { user: user, setUser: setUser }
return (
<ApolloProvider client={client}>
<UserContext.Provider value={contextVal}>
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AppNavigator />
</View>
</UserContext.Provider>
</ApolloProvider>
);
}
And the LoginScreen retrieves user and setUser from the UserContext and when it gets data from the sigin mutation, tries to pass a simple user object to setUser:
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [signin, { data, error, loading }] = useMutation(SIGNIN_MUTATION)
const { user, setUser } = useContext(UserContext)
useEffect(() => {
if (user) {
props.navigation.navigate('Main')
}
}, [user])
if (loading) return (
<View style={styles.main}>
<Text style={styles.text}>Logging you in!</Text>
<ActivityIndicator size='large' />
</View>
)
if (data) { // ERROR IS HERE
setUser({
token: data.signin.token,
name: data.signin.user.name,
})
}
return (
<View style={styles.main}>
<Text style={styles.welcome}>Welcome to Bravauto!</Text>
<Text style={styles.error}>{error ? errorMessage(error) : ''}</Text>
<Text>Please login to continue:</Text>
<TextInput style={styles.input} onChangeText={(text) => setEmail(text)} placeholder="email" />
<TextInput style={styles.input} onChangeText={(text) => setPassword(text)} placeholder="password" secureTextEntry={true} />
<Button title="Login!" onPress={() => signin({ variables: { email: email, password: password } })} />
<Button title="No Account? Register Here" onPress={() => props.navigation.navigate('Registration')} />
</View>
)
I remember that to get this working I had to wrap the "already have a user" case (where it calls props.navigate) in a useEffect call, and I found other posts suggesting that wrapping this code in useEffect will fix the warning. However if I wrap this code in a useEffect hook like this:
useEffect(() => {
if (data) {
setUser({
token: data.signin.token,
name: data.signin.user.name,
})
}
})
I get an error instead of a warning:
Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
Any help would be much appreciated!
You will want to include the data field in the list of ones the useEffect is using:
useEffect(() => {
if (data) {
setUser({
token: data.signin.token,
name: data.signin.user.name,
})
}
}, [data])
Oops, forgot to post that I eventually found the answer to this. The solution was to use the callbacks provided by Apollo:
const [signin, { loading }] = useMutation(SIGNIN_MUTATION, {
onCompleted: (data) => {
setAuth(data.signin.token);
},
onError: (errorMsg) => {
setError(errorMessage(errorMsg));
},
errorPolicy: "none"
});
After changing to this approach everything works fine.

Cannot access the correct this context inside the axios callback react native

I'm able to access the correct this context inside the signInWithEmail function, however the this context inside the callback function from axios is undefined. I'm able to call the user action SignInUser inside the component's render with: this.props.signInUser. But I want to call it based on the api response.
Here's the code from my component
import React, { Component } from 'react';
import { View, Text, StyleSheet, Button, TextInput } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { signInUser } from "../actions/UserActions"
import API from "../api/config";
class LoginForm extends Component {
constructor(props) {
super(props);
console.log(props)
this.state = { username: '', password: '' };
this.signInWithEmail = this.signInWithEmail.bind(this);
}
signInWithEmail(username, pass) {
console.log(this.props); // correct context here
API.post('/token/', {
username: username,
password: pass
}).then(function (response) {
console.log(response.data);
console.log(this.props); // undefined here
}).catch(function (error) {
// Show error or redirect
console.log(error);
});
}
render() {
return (
<View>
<Text>access: {this.props.userState.accessToken}</Text>
<Text>refres: {this.props.userState.isSignout.toString()}</Text>
<Text>username: {this.state.username}</Text>
<Text>password: {this.state.password}</Text>
<Text style={styles.inputLabel}> Username </Text>
<TextInput
style={styles.input}
autoCapitalize="none"
onChangeText={(username) => this.setState({ username })}
value={this.state.username}
/>
<Text style={styles.inputLabel}> Password </Text>
<TextInput
style={styles.input}
secureTextEntry
onChangeText={(password) => this.setState({ password })}
value={this.state.password}
/>
<Button title="Sign in with email" onPress={() => this.signInWithEmail(this.state.username, this.state.password)} />
{/* <Button title="Sign in with email" onPress={() => this.props.signInUser("dummy-token", "dummy-refresh")} /> */}
</View>
);
}
};
const styles = StyleSheet.create({
label: {
fontSize: 16,
fontWeight: 'normal',
marginBottom: 48,
borderColor: "#FFF222"
},
divider: {
borderBottomColor: '#000000',
borderBottomWidth: 1,
marginVertical: 10,
},
input: {
backgroundColor: '#F0EEEE',
height: 40,
borderRadius: 5,
marginBottom: 10,
fontSize: 18
},
inputLabel: {
fontSize: 18,
fontWeight: "bold"
}
});
const mapStateToProps = (state) => {
const { userState } = state
return { userState }
}
const mapDispatchToProps = dispatch => (
bindActionCreators({
signInUser,
}, dispatch)
);
export default connect(mapStateToProps, mapDispatchToProps)(LoginForm);
You can use arrow function (=>) inside the then callback. Inside the arrow function, the Context (this) will not be changed automatically. Try this:
API.post('/token/', {
username: username,
password: pass
}).then(response => {
console.log(response.data);
console.log(this.props);
}).catch(error => {
console.log(error);
});
N.B. Inside the Simple function (you are using as a .then() callback) Context (this) would be global/window!
Use arrow function to preserve context of react component
}).then((response) => {
console.log(response.data);
console.log(this.props); // undefined here
})....

Message not showing up in my react native chat bot

I'm making a chat bot app in react native and I am facing some difficulty in showing the messages that are saved in my firebase. The message box shows up empty. Moreover, three messages show up whereas there is only one message in my firebase. I am using React-Redux for state management and react-native-router-flux for navigation
Here is the code of my Login.js:
import React, {Component} from 'react'
import {View, Text, TouchableOpacity, StatusBar, ImageBackground} from 'react-native'
import {connect} from 'react-redux'
import {login, loginUser} from '../actions'
import {Input, Card, Button, Spinner} from './common'
class Login extends Component {
loginUser() {
const {email, password} = this.props
this.props.loginUser({email, password})
}
renderButton() {
if(this.props.loading) {
return <Spinner size='large' />
}
return (
<Button style={{backgroundColor: '#2D99FC', color: 'white'}}
text='Sign in'
onPress={this.loginUser.bind(this)}
/>
)
}
render() {
return(
<ImageBackground
style={{width: '100%', height: '100%'}}
source={require('../assets/images/bg.jpg')}
>
<StatusBar hidden />
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text style={{fontSize: 25, fontWeight: 'bold', margin: 10}}>Login</Text>
<Text style={{color: 'grey', margin: 10}}>Stay Connected</Text>
<Card>
<Input
placeholder='name#example.com'
iconName='user'
value={this.props.email}
onChangeText={value => this.props.login({prop: 'email', value})}
/>
<Input
placeholder="******"
iconName='lock'
secureTextEntry
value={this.props.password}
onChangeText={value => this.props.login({prop: 'password', value})}
/>
<Text style={{fontSize: 20, color: 'red'}}>
{this.props.error}
</Text>
{this.renderButton()}
<TouchableOpacity>
<Text style={{color:'#2D99FC', fontWeight: 'bold', margin: 10, marginBottom: 30}}>Forgot Password?</Text>
</TouchableOpacity>
<View style={{flexDirection: 'row', margin: 10}}>
<Text>Don't have an account? </Text>
<TouchableOpacity>
<Text style={{color:'#2D99FC', fontWeight: 'bold'}}>Sign Up</Text>
</TouchableOpacity>
</View>
</Card>
</View>
</ImageBackground>
);
}
}
const mapStateToProps = state => {
const {email, password, loading, error} = state.auth
return {email, password, loading, error}
}
export default connect(mapStateToProps, {login, loginUser})(Login)
Here is my code of AuthAction.js:
import {AsyncStorage} from 'react-native'
import firebase from 'firebase'
import {Actions} from 'react-native-router-flux'
import User from '../User'
import {REGISTER, SIGN_UP_USER, SIGN_UP_USER_SUCCESS, SIGN_UP_USER_FAIL, LOGIN, LOGIN_USER, LOGIN_USER_SUCCESS, LOGIN_USER_FAIL, UPDATE_USERS} from './types'
export const register = ({prop, value}) => {
return {
type: REGISTER,
payload: {prop, value}
}
}
export const createUser = ({email, password, conPass, name}) => {
return(dispatch) => {
dispatch({type: SIGN_UP_USER})
if(password !== conPass) {
signupUserFail(dispatch, 'Passwords do not match')
}
else {
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(user => {
signupUserSuccess(dispatch, user)
addData(name, email)
})
.catch(() => signupUserFail(dispatch, 'Authentication Failed'))
}
}
}
const addData = async (name, email) => {
await AsyncStorage.setItem('userName', name)
await AsyncStorage.setItem('userEmail', email)
User.name = name
User.email = email
firebase.database().ref('users/' + User.name).set({name, email})
}
const signupUserSuccess = (dispatch, user) => {
dispatch({
type: SIGN_UP_USER_SUCCESS,
payload: user
})
}
const signupUserFail = (dispatch, error) => {
dispatch({
type: SIGN_UP_USER_FAIL,
payload: error
})
}
export const login = ({prop, value}) => {
return {
type: LOGIN,
payload: {prop, value}
}
}
export const loginUser = ({email, password}) => {
return(dispatch) => {
dispatch({type: LOGIN_USER})
firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch(() => loginUserFail(dispatch, 'Authentication Failed'))
}
}
const loginUserSuccess = (dispatch, user) => {
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user
})
firebase.database().ref('users')
.on('child_added', (val) => {
let person = val.val()
person.name = val.key
dispatch({type: UPDATE_USERS, payload: person})
})
Actions.chat()
}
const loginUserFail = (dispatch, error) => {
dispatch({
type: LOGIN_USER_FAIL,
payload: error
})
}
Here is my code to AuthReducer.js:
import {REGISTER, SIGN_UP_USER, SIGN_UP_USER_SUCCESS, SIGN_UP_USER_FAIL, LOGIN, LOGIN_USER, LOGIN_USER_SUCCESS, LOGIN_USER_FAIL, UPDATE_USERS} from '../actions/types'
import User from '../User'
const INITIAL_STATE = {email: '', password: '', conPass: '', loading: false, error: '', user: null, name: '', user: []}
export default (state = INITIAL_STATE, action) => {
console.log(action)
switch(action.type) {
//Registration
case REGISTER:
return {...state, [action.payload.prop]: action.payload.value}
case SIGN_UP_USER:
return {...state, loading: true, error: ''}
case SIGN_UP_USER_SUCCESS:
return {...state, ...INITIAL_STATE, user: action.payload}
case SIGN_UP_USER_FAIL:
return {...state, error: action.payload, password: '', conPass: '', loading: false}
//Login
case LOGIN:
return {...state, [action.payload.prop]: action.payload.value}
case LOGIN_USER:
return {...state, loading: true, error: ''}
case LOGIN_USER_SUCCESS:
return {...state, ...INITIAL_STATE, user: action.payload}
case LOGIN_USER_FAIL:
return {...state, error: action.payload, password: '', loading: false}
case UPDATE_USERS:
User.name = action.payload.name
User.email = action.payload.email
//default
default:
return state
}
}
Here is my code to Chat.js:
import React, {Component} from 'react'
import {View, SafeAreaView, Text, TextInput, TouchableOpacity, StatusBar, Dimensions, FlatList} from 'react-native'
import {connect} from 'react-redux'
import {messageChanged, sendMessage, messagesListFetch} from '../actions'
import { Header } from './common';
import User from '../User';
class Chat extends Component {
onMessageChange(text) {
this.props.messageChanged(text)
}
sendMessage() {
const {message} = this.props
this.props.sendMessage(message)
}
componentDidMount() {
this.props.messagesListFetch()
}
render() {
return(
<View>
<View>
<StatusBar hidden />
<Header text='Chat with Me' />
</View>
<SafeAreaView>
<FlatList
scrollEnabled
style={{padding: 10, height: Dimensions.get('window').height * 0.8}}
data={this.props.messageList}
renderItem={(item) => {
return(
<View style={{
flexDirection: 'row',
width: '60%',
alignSelf: item[0] === User.name ? 'flex-end' : 'flex-start',
backgroundColor: item[0] === User.name ? '#00897B' : '#7CB342',
borderRadius: 5,
marginBottom: 10
}}>
<Text style={{color: '#FFF', padding: 7, fontSize: 16}}>{item[1]}</Text>
<Text style={{color: '#EEE', padding: 3, fontSize: 12}}>{item[2]}</Text>
</View>
)
}}
keyExtractor={(item, index) => item[0]}
/>
<View style={{flexDirection: 'row', alignItems: 'center', marginHorizontal: 5}}>
<TextInput
style={styles.input}
placeholder='Type a message...'
value={this.props.message}
onChangeText={this.onMessageChange.bind(this)}
/>
<TouchableOpacity onPress={this.sendMessage.bind(this)} style={{paddingBottom: 10, marginLeft: 5}}>
<Text>Send</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
</View>
);
}
}
const styles = {
input: {
padding: 10,
borderWidth: 1,
borderColor: '#CCC',
width: '80%',
marginBottom: 10,
borderRadius: 5,
},
}
const mapStateToProps = state => {
let messages = state.chat.messageList
let messageList = []
Object.keys(messages).map((key, index) => {
messageList.push(messages[key])
})
let message = state.chat.message
return {message, messageList}
}
export default connect(mapStateToProps, {messageChanged, sendMessage, messagesListFetch})(Chat)
Here is my code to ChatActions.js:
import firebase from 'firebase'
import {MESSAGE_CHANGED, MESSAGE_SENT, GET_MESSAGE_LIST} from './types'
import User from '../User'
export const messageChanged = (text) => {
return {
type: MESSAGE_CHANGED,
payload: text
}
}
export const sendMessage = (textMessage) => {
return(dispatch) => {
if(textMessage.length > 0) {
console.log(User.name)
let msgId = firebase.database().ref('messages').child(User.name).child('Bot').push().key
let updates = {}
let message = {
message: textMessage,
time: firebase.database.ServerValue.TIMESTAMP,
from: User.name
}
updates['messages/' + User.name + '/' + 'Bot' + '/' + msgId] = message
updates['messages/' + 'Bot' + '/' + User.name + '/' + msgId] = message
firebase.database().ref().update(updates)
dispatch({type: MESSAGE_SENT})
}
}
}
export const messagesListFetch = () => {
return(dispatch) => {
setTimeout(() => {
firebase.database().ref('messages').child(User.name).child('Bot')
.on('child_added', (value) => {
dispatch({type: GET_MESSAGE_LIST, payload: value.val()})
// console.log(value.val())
})
}, 1000)
}
}
Here is my code to ChatReducer.js:
import {MESSAGE_CHANGED, MESSAGE_SENT, GET_MESSAGE_LIST} from '../actions/types'
const INITIAl_STATE = {message: '', messageList: []}
export default (state = INITIAl_STATE, action) => {
switch(action.type) {
case MESSAGE_CHANGED:
return {...state, message: action.payload}
case MESSAGE_SENT:
return {...state, message: ''}
case GET_MESSAGE_LIST:
return {...state, messageList: action.payload}
default:
return state
}
}
Here is my User.js:
User = {
name: '',
email: ''
}
export default User
I want the message to show, and to show only once, not thrice. How can I do that? Can anyone help me here?

Stack navigator giving me undefined error

I'm using https://facebook.github.io/react-native/docs/navigation.html by the way.
I'm trying to use the StackNavigator to go from Login.js to AboutDendro.js. What's wrong in my <Button/> component that's throwing that error in my iOS simulator?
Here's Login.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ScrollView, Text, TextInput, View, Button, StyleSheet } from 'react-native';
import { login } from '../redux/actions/auth';
import {AuthenticationDetails, CognitoUser, CognitoUserAttribute, CognitoUserPool} from '../lib/aws-cognito-identity';
import StackNavigator from 'react-navigation';
import AboutDendro from './AboutDendro';
const awsCognitoSettings = {
UserPoolId: 'something',
ClientId: 'something'
};
class Login extends Component {
constructor (props) {
super(props);
this.state = {
page: 'Login',
username: '',
password: ''
};
}
get alt () { return (this.state.page === 'Login') ? 'SignUp' : 'Login'; }
handleClick (e) {
e.preventDefault();
const userPool = new CognitoUserPool(awsCognitoSettings);
// Sign up
if (this.state.page === 'SignUp') {
const attributeList = [
new CognitoUserAttribute({ Name: 'email', Value: this.state.username })
];
userPool.signUp(
this.state.username,
this.state.password,
attributeList,
null,
(err, result) => {
if (err) {
alert(err);
this.setState({ username: '', password: '' });
return;
}
console.log(`result = ${JSON.stringify(result)}`);
this.props.onLogin(this.state.username, this.state.password);
}
);
} else {
const authDetails = new AuthenticationDetails({
Username: this.state.username,
Password: this.state.password
});
const cognitoUser = new CognitoUser({
Username: this.state.username,
Pool: userPool
});
cognitoUser.authenticateUser(authDetails, {
onSuccess: (result) => {
console.log(`access token = ${result.getAccessToken().getJwtToken()}`);
this.props.onLogin(this.state.username, this.state.password);
},
onFailure: (err) => {
alert(err);
this.setState({ username: '', password: '' });
return;
}
});
}
}
togglePage (e) {
this.setState({ page: this.alt });
e.preventDefault();
}
static navigationOptions = {
title: 'AboutDendro',
};
render() {
const { navigate } = this.props.navigation;
const App = StackNavigator({
Home: { screen: Login },
Profile: { screen: AboutDendro },
});
return (
<ScrollView style={{padding: 20}}>
<Button
title="Go to Jane's profile"
onPress={() =>
navigate('AboutDendro', { name: 'AboutDendro' })
}
/>
<Text style={{fontSize: 27}}>{this.state.page}</Text>
<TextInput
placeholder='Email Address'
autoCapitalize='none'
autoCorrect={false}
autoFocus={true}
keyboardType='email-address'
value={this.state.username}
onChangeText={(text) => this.setState({ username: text })} />
<TextInput
placeholder='Password'
autoCapitalize='none'
autoCorrect={false}
secureTextEntry={true}
value={this.state.password}
onChangeText={(text) => this.setState({ password: text })} />
<View style={{margin: 7}}/>
<Button onPress={(e) => this.handleClick(e)} title={this.state.page}/>
<View style={styles.firstView}>
<Text onPress={(e) => this.togglePage(e)} style={styles.buttons}>
{this.alt}
</Text>
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
buttons: {
fontSize: 12,
color: 'blue',
flex: 1
},
firstView: {
margin: 7,
flexDirection: 'row',
justifyContent: 'center'
}
});
const mapStateToProps = (state, ownProps) => {
return {
isLoggedIn: state.auth.isLoggedIn
};
}
const mapDispatchToProps = (dispatch) => {
return {
onLogin: (username, password) => { dispatch(login(username, password)); }
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
It is because navigation is not in your props. It is a part of your App component you created. But you do nothing with this component.
You should have an App.js file, with your stackNavigator, set your Login component as your default component in your stackNavigator's parameters.
Take a look at this documentation
I try to refactor your code.
in component render maybe you can just write :
render() {
const { navigate } = this.props.navigation;
return (
<ScrollView style={{padding: 20}}>
<Button
title="Go to Jane's profile"
onPress={() =>
navigate('Profile', { name: 'AboutDendro' })
}
/>
<Text style={{fontSize: 27}}>{this.state.page}</Text>
<TextInput
placeholder='Email Address'
autoCapitalize='none'
autoCorrect={false}
autoFocus={true}
keyboardType='email-address'
value={this.state.username}
onChangeText={(text) => this.setState({ username: text })} />
<TextInput
placeholder='Password'
autoCapitalize='none'
autoCorrect={false}
secureTextEntry={true}
value={this.state.password}
onChangeText={(text) => this.setState({ password: text })} />
<View style={{margin: 7}}/>
<Button onPress={(e) => this.handleClick(e)} title={this.state.page}/>
<View style={styles.firstView}>
<Text onPress={(e) => this.togglePage(e)} style={styles.buttons}>
{this.alt}
</Text>
</View>
</ScrollView>
);
}
}
And you separate component StackNavigation below component render() like:
const App = StackNavigator({
Home: { screen: Login },
Profile: { screen: AboutDendro },
});
And then import component App in your index.ios.js like:
import './Login';
just that. Maybe my answer can help you.

Categories