Cannot Navigate Away from Component - react-native

I cannot seem to navigate away from my component despite trying both this.props.navigation.navigate and NavigationService
To test it out, i created a simple button to navigate away and it work but when i used it within my formik and react-native, it did not navigate away
Anyone encountered such situations before? How do you resolve? below is my code
thank you
import React, { Component } from "react";
import {
View,
StyleSheet,
AsyncStorage,
Text,
TouchableHighlight
} from "react-native";
import { Button } from "react-native-elements";
import { Formik } from "formik";
import * as Yup from "yup";
import { Mutation } from "react-apollo";
import Input from "../components/Input";
import NavigationService from "../../navigation/NavigationService";
import { SIGNIN } from "../../graphql/queries";
const FormValidationSchema = Yup.object().shape({
email: Yup.string()
.email("Not valid email")
.required("Email is required"),
password: Yup.string()
.min(3)
.required("Password is required")
});
class EmailSigninScreen extends Component {
_handleSubmit = async (values, bag, signin) => {
console.log(values, bag, signin);
try {
const data = await signin({
variables: {
email: values.email,
password: values.password
}
});
await AsyncStorage.setItem("token", data.signin.token);
this.props.navigation.navigate("Main");
// NavigationService.navigate("Main");
} catch (error) {
bag.setSubmitting(false);
bag.setErrors(error);
}
};
render() {
const { container, buttonStyle } = styles;
return (
<View style={container}>
<Mutation mutation={SIGNIN}>
{(signin, { error }) => {
console.log(error);
return (
<Formik
initialValues={{ email: "", password: "" }}
onSubmit={(values, bag) =>
this._handleSubmit(values, bag, signin)
}
validationSchema={FormValidationSchema}
render={({
values,
handleSubmit,
errors,
touched,
setFieldValue,
setFieldTouched,
isValid,
isSubmitting
}) => (
<React.Fragment>
<Input
label="Email"
autoCapitalize="none"
autoComplete="none"
value={values.email}
onChange={setFieldValue}
onTouch={setFieldTouched}
name="email"
error={touched.email && errors.email}
/>
<Input
label="Password"
autoComplete={false}
autoCapitalize="none"
secureTextEntry
name="password"
value={values.password}
onChange={setFieldValue}
onTouch={setFieldTouched}
error={touched.password && errors.password}
/>
<Button
backgroundColor="purple"
buttonStyle={buttonStyle}
title="Submit"
onPress={handleSubmit}
disabled={!isValid || isSubmitting}
loading={isSubmitting}
/>
<TouchableHighlight
onPress={() => this.props.navigation.navigate("Main")}
>
<Text>Jump</Text>
</TouchableHighlight>
</React.Fragment>
)}
/>
);
}}
</Mutation>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center"
// justifyContent: "center"
},
buttonStyle: {
marginTop: 20,
width: "100%"
}
});
export default EmailSigninScreen;

Try to use withNavigation like this:
import { withNavigation } from 'react-navigation';
export default withNavigation(EmailSigninScreen);
everything else can stay the same

The problem lies in my async - await syntax factoring
factoring my handleSubmit function to below format works...
i am still trying to see if i can convert the codes below to purely using async and await rather than a mixture of async and await & then and catch. if anyone has a solution, will appreciate advise here. Tq
//TODO refactor to async and await syntax
_handleSubmit = (values, bag, signin) => {
signin({
variables: {
email: values.email,
password: values.password
}
})
.then(async ({ data }) => {
if (data && data.signin.token) {
await AsyncStorage.setItem("token", data.signin.token);
this.props.navigation.navigate("Main");
}
})
.catch(err => {
bag.setSubmitting(false);
bag.setErrors(err);
});
};

Related

How to deal with delay api response in react native using redux?

Im building a react-native app, which when the user try to signIn, I invoike firebase.CreateUser and then an api from firebase function to create that user in my database (Firebase Real-Time). The problem is that when the componentDidUpdate is executed, I still don't have the result from my firebaseFunction, then my props only update if I tap in screen. I would like to know how to deal with that.
Im using redux.
Follow my code:
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, Button, Image,Alert} from 'react-native';
import logo from '../../asserts/logo.png'
import { TouchableOpacity, TextInput } from 'react-native-gesture-handler';
import { Divider,Input} from 'react-native-elements';
import axios from 'axios';
import { connect } from 'react-redux';
import { signupUser} from '../../store/actions/Section/actions';
class Signin extends Component {
state = {
fullName:'',
userName:'',
email:'',
password:'',
confirmPassword:'',
bornDate:'',
State:'',
City:''
};
handleEmailChange = val => {
this.setState({ email:val });
};
handlePasswordChange = val => {
this.setState({ password:val });
};
handleConfirmPasswordChange = val => {
this.setState({ confirmPassword:val });
};
handleNameChange = val => {
this.setState({ fullName:val });
};
handleUserNameChange = val => {
this.setState({ userName:val });
};
handleStateChange = val => {
this.setState({ State:val });
};
handleCityChange = val => {
this.setState({ City:val });
};
handleBornDateChange = val => {
this.setState({ bornDate:val });
};
onSignInUser = () => {
const {email,password} = this.state
if(email=='' || password=='')
return;
this.props.signUp(this.state.fullName,this.state.userName, this.state.email,this.state.password,this.state.confirmPassword,this.state.bornDate,this.state.State,this.state.City);
// this.props.navigation.navigate('User');
};
componentDidUpdate() {
const { idUser, loading,error } = this.props;
console.log(idUser);
console.log('aqui');
if (!loading && error) Alert.alert('Erro', error);
if (!loading && idUser) this.props.navigation.navigate('User');
}
render() {
return (
<View style={styles.container}>
<View style={styles.flexCenter}>
<Image source={logo} style={styles.logoImage}/>
<Text style={styles.logoText} >HomeShare</Text>
<Text style={styles.sublogoText} >SignUp</Text>
</View>
<Divider style={styles.divider} />
<View style={styles.flexButton}>
<View style={styles.inputContainer}>
<Input style={styles.textInput} onChangeText={this.handleNameChange} value={this.state.fullName} placeholder='Nome'/>
<Input style={styles.textInput} onChangeText={this.handleUserNameChange} value={this.state.userName} placeholder='User'/>
<Input style={styles.textInput} onChangeText={this.handleBornDateChange} value={this.state.bornDate} placeholder='Nascimento'/>
<Input style={styles.textInput} onChangeText={this.handleStateChange} value={this.state.State} placeholder='Estado'/>
<Input style={styles.textInput } onChangeText={this.handleCityChange} value={this.state.City} placeholder='Cidade'/>
<Input style={styles.textInput} onChangeText={this.handleEmailChange} value={this.state.email} placeholder='E-mail' keyboardType={'email-address'}/>
<Input style={styles.textInput} onChangeText={this.handlePasswordChange} value={this.state.password} placeholder='Senha' secureTextEntry={true}/>
<Input style={styles.textInput} onChangeText={this.handleConfirmPasswordChange} value={this.state.confirmPassword} placeholder='Confirme sua Senha' secureTextEntry={true}/>
</View>
<TouchableOpacity style={styles.button} activeOpacity={0.5} onPress={this.onSignInUser} >
<View>
<Text style={styles.buttonText}>SignIn</Text>
</View>
</TouchableOpacity>
<Text style={{marginTop:10}}>Ou</Text>
<TouchableOpacity style={styles.button} activeOpacity={0.5} onPress={this.signInUser}>
<View>
<Text style={styles.buttonText}>Entrar com Facebook</Text>
</View>
</TouchableOpacity>
</View>
</View>
);
}
}
const mapStateToProps = ({ section: { restoring, loading, user, error, logged, idUser } }) => ({
restoring: restoring,
loading: loading,
user: user,
error: error,
logged: logged,
idUser: idUser
});
const mapDispatchToProps = {
signUp:signupUser
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Signin);
My Action:
export const signupUser = (fullName,userName, email,password,confirmPassword,bornDate,State,City) => dispatch => { dispatch(sessionLoading());
firebaseService.auth().createUserWithEmailAndPassword(email, password).then(user => {
console.log(user);
firebaseService.auth().currentUser.getIdToken(true).then(function(idToken) {
SectionService.signIn(idToken,fullName,userName, email,password,confirmPassword,bornDate,State,City).then((response) =>{
console.log(response);
dispatch(sessionSetId(response));
}).catch(e=> {
dispatch(sessionError(e));
});
}).catch(function(error) {
dispatch(sessionError(e));
});
})
.catch(error => {
dispatch(sessionError(error.message));
});
A proposed solution is to handle the account creation in the createUser callback and to update it with other data in the cloud function. Alternatively you can set up a listener that looks for the document, which will then be created and the listener will be notified.
I personally create the user doc on the client side because I create it with some data only available on the client, but your use case will be dictate your preferred approach.

React Redux is not updating the state

I am searching for a couple of days and I can't find out why redux is not updating the state I don't see any problem in my code please help me to find the problem.
it is a simple login project. I can see that data changes inside the reducer when debugging but it's not being mapped to props and state is not changing.
this is my code:
actions.js
import {
LOGIN_SUCCESS,
LOGIN_FAILURE,
} from './types'
export const loginSuccess = (user) => ({
type: LOGIN_SUCCESS,
payload: user
})
export const loginFailure = (error) => ({
type: LOGIN_FAILURE,
payload: error
})
loginReducer.js
import {
LOGIN_SUCCESS,
LOGIN_FAILURE,
} from './types'
const initialState = {
user: null,
errorMessage: null
}
export const loginReducer = (state = initialState, action) => {
switch (action.type) {
case LOGIN_SUCCESS:
return {...state,user: action.payload, errorMessage: null }
case LOGIN_FAILURE:
return {...state,errorMessage: action.payload }
default:
return state;
}
}
loginScreen.js
import React from 'react'
import {
Text,
View,
ImageBackground,
Button,
StyleSheet
} from 'react-native'
import { TextField } from 'react-native-material-textfield';
import { loginSuccess, loginFailure } from './reduxx/actions'
import { connect } from "react-redux";
class LoginScreen extends React.Component {
constructor(props) {
super(props)
this.state = {
email: "",
password: "",
}
}
_handlePress() {
this.props.login(this.state.email, this.state.password)
// user in propa is undifined
this.props.user
}
render() {
let { email, password } = this.state
return (
<ImageBackground source={require('./images/loginBackground.jpg')} style={styles.imageBackground}>
<View style={styles.mainContainer}>
<View style={styles.box}>
<TextField label="Email" value={email} onChangeText={email => this.setState({ email })} />
<TextField label="Password" textContentType="password" value={password} onChangeText={password => this.setState({ password })} />
<Button onPress={() => {
this._handlePress()
}} color="green" title="Sign in" style={styles.button} />
</View>
<View style={styles.bottomTextContainer}>
<Text style={{ color: "white" }}>Don't have an account?</Text>
<Text style={{ color: "lightgreen" }} onPress={() => this.props.navigation.navigate("Register")}> Sign up</Text>
</View>
</View>
</ImageBackground>
)
}
}
function mapStateToProps(state) {
return {
user: state.user,
errorMessage: state.errorMessage
}
}
function mapDispatchToProps(dispatch) {
return {
login: (email, password) => {
try {
let user = //get user from database
dispatch(loginSuccess(user))
} catch (error) {
dispatch(loginFailure(error))
}
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
const styles = StyleSheet.create({
imageBackground: {
width: '100%',
height: '100%'
},
mainContainer: {
flex: 1,
justifyContent: "center"
},
box: {
backgroundColor: 'white',
padding: 8,
margin: 8
},
button: {
margin: 10
},
bottomTextContainer: {
position: "absolute",
bottom: 8,
alignSelf: "center",
flexDirection: "row"
}
});
app.js
import React from 'react';
import { Provider } from "react-redux"
import store from "./reduxx/store";
import AppContainer from './Navigator'
export default class App extends React.Component {
render() {
return (
<Provider store={store} >
<AppContainer />
</Provider>
);
}
}
store.js
import {createStore} from "redux";
import { combineReducers } from 'redux';
import {loginReducer} from './loginReducer'
const rootReducer = combineReducers({
loginReducer,
});
export default store = createStore(rootReducer)
The state which comes into mapStateToProps contains all the combined reducers, so we need to access the reducer name first from the state (like state.loginReducer.user), before trying to access the data of that reducer.
PFB code which should work:
function mapStateToProps(state) {
return {
user: state.loginReducer.user,
errorMessage: state.loginReducer.errorMessage
}
}
Change here :
function mapStateToProps(state) {
return {
user: state.user,
errorMessage: state.errorMessage
}
}
TO
function mapStateToProps(state) {
return {
login: state.loginReducer
}
}
And then this.props.user to this.props.login.user and this.props.errorMessage to this.props.login.errorMessage in all the occurrence.

Check if function is dispatching action with jest and enzyme

I have a Login Screen that has a method that when called dispatches an action. I want to test using jest and enzyme if when the button is pressed, this function is called and, therefore, the action is dispatched. I tried in many different ways, but I couldn't achieve this.
screens/login.js
import React, { Component } from 'react';
import {
KeyboardAvoidingView,
TextInput,
StyleSheet,
Text,
Button,
TouchableOpacity
} from 'react-native';
import { connect } from 'react-redux';
import { login } from 'actions/sessions.js';
export class LoginScreen extends Component {
constructor(props){
super(props);
this.state = {
email: '',
password: '',
error: ''
}
}
requestLogin = async () => {
if(this.checkFields()){
this.setState({error: ''});
this.props.loginRequest(this.state.email, this.state.password);
}
}
checkFields = () => {
let { email, password } = this.state;
const re = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if(!re.test(email)){
this.setState({error: 'E-mail must be valid'})
return false;
}else if(email == '' || password == ''){
this.setState({error: 'All fields are required!'});
return false
}else{
return true;
}
}
handleEmailText = (email) => {
this.setState({email})
}
handlePasswordText = (password) => {
this.setState({password})
}
render(){
return(
<KeyboardAvoidingView style={styles.container} enabled={true} behavior="padding">
<Text>{this.state.error || this.props.error}</Text>
<TextInput
onChangeText={(e) => this.handleEmailText(e)}
value={this.state.email}
keyboardType="email-address"
textContentType="emailAddress"
autoCapitalize="none"
placeholder="E-mail"
style={styles.input}
/>
<TextInput
onChangeText={(e) => this.handleEmailPassword(e)}
value={this.state.password}
placeholder="Password"
textContentType="password"
autoCapitalize="none"
secureTextEntry={true}
style={styles.input}
/>
<Button style={styles.button}
title="Sign In"
onPress={() => this.requestLogin()}/>
</KeyboardAvoidingView>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
input: {
width: "50%",
height: 40,
borderColor: 'gray',
borderWidth: 1,
paddingLeft: 20,
paddingRight: 20,
margin: 10
}
})
const mapDispatchToProps = dispatch => {
return {
loginRequest: (email, password) => dispatch(login(email, password))
}
}
const mapStateToProps = state => {
return {
error: state.sessions.error
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
tests/screens/login.test.js
import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import renderer from 'react-test-renderer';
import configureStore from 'redux-mock-store';
import { LoginScreen } from 'screens/Login';
describe('LoginScreen', () => {
it('should dispatch login action when button clicked', async () => {
const mockStore = configureStore();
const initialState = {};
const store = mockStore(initialState);
const wrapper = shallow(<LoginScreen store={store}/>)
wrapper.setState({email: 'foo#bar.com', password: '1234'})
const component = await wrapper.dive();
component.find('button').simulate('click');
expect(store.getActions()).toMatchSnapshot();
});
})
When I have this approach, it says that simulate is meant to be run on 1 node. 0 found instead. I have no clue how to test it.
In react native you can use:
component.find('Button').props().onPress();
to simulate user interaction.
Also, you should use Button instead of button (your component name).

React Native Checkbox not working

I have my react-native app consisting of a form. I have implemented two check-boxes with their state set to boolean true. I want boolean true or false to get submitted to the database from those check-boxes but the check-box button won't work.
Here's my code:
import React, { Component } from 'react';
import { View, Text, Button, TextInput, StyleSheet } from 'react-native';
import { CheckBox } from 'react-native-elements';
import axios from 'axios';
export default class Delivery extends Component {
constructor(props) {
super(props);
this.state = {
trackingNo: '',
weight: '',
length: '',
width: '',
//checked: true,
delivered: { checked: true }
};
}
componentDidMount(){
fetch('https://courierapp-test.herokuapp.com/products/')
.then(
function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
// Examine the text in the response
response.json().then(function(data) {
console.log(data);
});
}
)
.catch(function(err) {
console.log('Fetch Error :-S', err);
});
}
onPressSubmit(){
axios.post('https://courierapp-test.herokuapp.com/products/', {
requestId: this.state.trackingNo,
parcelWeight: this.state.weight,
parcelLength: this.state.length,
parcelWidth: this.state.width,
parcelPickup: this.state.pickup,
parcelDelivered: this.state.delivered,
})
.then(function (response) {
console.log(response, "data sent");
})
.catch(function (error) {
console.log(error, "data not sent");
});
}
render() {
return (
<View style={{padding: 15}}>
<TextInput
style={styles.inputStyle}
placeholder="Request Id"
onChangeText={(trackingNo) => this.setState({trackingNo})}
value={this.state.trackingNo}
/>
<CheckBox style={styles.checkStyle}
title='Pickup'
checked={this.state.checked}
/>
<CheckBox style={styles.checkStyle}
title='Delivered'
onValueChange={() => this.setState({ checked: !this.state.checked })}
/>
<TextInput
style={styles.inputStyle}
placeholder="Enter Parcel Weight(Kg)"
onChangeText={(weight) => this.setState({weight})}
value={this.state.weight}
/>
<TextInput
style={styles.inputStyle}
placeholder="Enter Parcel Length(Cm)"
onChangeText={(length) => this.setState({length})}
value={this.state.length}
/>
<TextInput
style={styles.inputStyle}
placeholder="Enter Parcel Width(Cm)"
onChangeText={(width) => this.setState({width})}
value={this.state.width}
/>
<Button
onPress={() => {
this.onPressSubmit()
}
}
title="Submit"
color="#1ABC9C"
/>
</View>
);
}
}
const styles = StyleSheet.create({
inputStyle: {
color: '#1ABC9C',
height: 40,
borderWidth: 0
}
});
I have tried every work around but could not solve it. the checkbox keeps on blinking only upon clicking but could not change the state checked or unchecked and also no value getting submitted to database.
Define variable checked in state
this.state = {
checked: false
};
create a method
onChangeCheck() {
this.setState({ checked: !this.state.checked})
}
And use onChange() prop of Chekbox to update the state and provide value to the check box like this
<CheckBox
style={styles.checkBox}
value={this.state.checked}
onChange={() => this.onChangeCheck()} />
Try changing value inside event onValueChange
<CheckBox value={this.aBooleanField} onValueChange={() => {
this.aBooleanField = !this.aBooleanField;
this.updateView();
}}/>
The updateView is like this:
updateView() {
this.setState({ viewChanged: !this.state.viewChanged})
}
Also works for < Switch /> component, and you can use updateView to only to refresh the view, setState it's to keep state values (when you resume app, rotate screen, etc).

React Native - mapStateToProps called but no re-rendering

I started React Native / Redux development for two weeks now, and i have some issues with the mapStateToProps and re-rendering.
Here are the facts :
mapStateToProps is called correctly with the right state (the new one)
render function is not called after the mapStateToProps
I have already check the troubleshooting page of Redux about that, and i dont have mutable issue
Here is the code :
Component
import React from 'react';
import { connect } from 'react-redux';
import { View, StyleSheet } from 'react-native';
import { Item, Input, Button, Text } from 'native-base';
import { signup } from './../actions/signup.action';
class Signup extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
confirmPassword: ''
};
}
render() {
console.log('rendering', this.props);
return (
<View style={styles.container}>
<Item rounded>
<Input placeholder='Email' onChangeText={(text) => this.setState({email: text})} value={this.state.email} />
</Item>
<Item rounded>
<Input placeholder='Mot de passe' secureTextEntry={true} onChangeText={(text) => this.setState({password: text})} value={this.state.password} />
</Item>
<Item rounded>
<Input placeholder='Confirmation du mot de passe' secureTextEntry={true} onChangeText={(text) => this.setState({confirmPassword: text})} value={this.state.confirmPassword} />
</Item>
<Button rounded onPress={(e) => this.onSignup(this.state.email, this.state.password)}>
<Text>Créer un compte</Text>
</Button>
<Text>{this.props.isSignupLoading}</Text>
<Text>{this.props.signupError}</Text>
</View>
)
}
onSignup = (email, password) => {
this.props.signup(email, password);
}
}
const mapStateToProps = (state) => {
return {
isSignupLoading: state.isSignupLoading,
isSignupSuccess: state.isSignupSuccess,
signupError: state.signupError
};
};
const mapDispatchToProps = (dispatch) => {
return {
signup: (email, password) => dispatch(signup(email, password))
};
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#F5FCFF",
},
});
export default connect(mapStateToProps, mapDispatchToProps)(Signup);
Actions
import { firebaseAuth } from './../config/firebase';
export const signupLoading = (isSignupLoading) => {
return {
type: 'SIGNUP_LOADING',
isSignupLoading
};
}
export const signupSuccess = (isSignupSuccess) => {
return {
type: 'SIGNUP_SUCCESS',
isSignupSuccess
};
};
export const signupError = (error) => {
return {
type: 'SIGNUP_ERROR',
signupError: error.message
};
};
export const signup = (email, password) => {
return (dispatch) => {
dispatch(signupLoading(true));
firebaseAuth.createUserWithEmailAndPassword(email, password)
.then(() => dispatch(signupLoading(false)))
.then(() => {
console.log("signup.action#signup success");
dispatch(signupSuccess(true));
})
.catch((error) => {
console.log("signup.action#signup failed - " + error);
dispatch(signupLoading(false));
dispatch(signupError(error));
});
};
};
Reducer
const defaultState = {
isSignupLoading: false,
isSignupSuccess: false,
signupError: null
};
const signupReducer = (state = defaultState, action) => {
switch (action.type) {
case 'SIGNUP_LOADING':
return Object.assign({}, state, {
isSignupLoading: action.isSignupLoading
});
case 'SIGNUP_SUCCESS':
return Object.assign({}, state, {
isSignupSuccess: action.isSignupSuccess
});
case 'SIGNUP_ERROR':
return Object.assign({}, state, {
signupError: action.signupError
});
default:
return state;
}
}
export default signupReducer;
If you want more code about what i have done, ask me.
Thanks for your help.
Best regards.
I met the same issue as you. You should fix it by modifying your mapStateToProps as following. This is due to signupReducer/signupReducer/signupError is under signupReducer not state.
const mapStateToProps = (state) => {
return {
isSignupLoading: state.signupReducer.isSignupLoading,
isSignupSuccess: state.signupReducer.isSignupSuccess,
signupError: state.signupReducer.signupError
};
};