react navigation "navigate" function not working inside firebase Asynchronous login function - firebase

I'm following the famous tutorial of "the-complete-react-native-and-redux-course",
i'm implementing react-navigation with redux and firebase, my problem is that its using the obsolete "react-native-router-flux" approach, i tried to follow it, but the project is not building at all.
I tried to migrate it step by step the the official react-navigation, the problem is, when trying to navigate from inside Asynchronous function (like firebase login function), navigation is not working, (putting in mind that i'm using redux)
**Update: Solution found, below is working code:
here is my Asynchronous function code (inside actions.js):
import firebase from 'firebase';
import { NavigationActions } from 'react-navigation'
import {
EMAIL_CHANGED,
PASSWORD_CHANGED,
LOGIN_USER_SUCCESS,
LOGIN_USER_FAIL,
LOGIN_USER
} from './types';
export const emailChanged = (text) => {
return {
type: EMAIL_CHANGED,
payload: text
};
};
export const passwordChanged = (text) => {
return {
type: PASSWORD_CHANGED,
payload: text
};
};
export const loginUser = ({email, password}, NavigationActions) => {
return (dispatch) => {
//indicate start user log in process
dispatch({type: LOGIN_USER});
firebase.auth().signInWithEmailAndPassword(email,password)
.then(user => loginUserSuccess(dispatch, user, NavigationActions))
.catch(() => {
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user, NavigationActions))
.catch(() => loginUserFail(dispatch));
});
};
};
const loginUserFail = (dispatch) => {
dispatch({type: LOGIN_USER_FAIL});
}
const loginUserSuccess = (dispatch, user, NavigationActions) => {
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user,
});
NavigationActions.navigate({ routeName: 'employeeList' })
};
and my router file:
import React from 'react';
import { StackNavigator } from 'react-navigation';
import LoginForm from './components/LoginForm';
import EmployeeList from './components/EmployeeList';
const Router = StackNavigator({
login: { screen: LoginForm },
employeeList: {screen: EmployeeList },
},
{
initialRouteName: 'login',
});
export default Router;
and App.js :
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reducers from './reducers';
import firebase from 'firebase';
import ReduxThunk from 'redux-thunk';
//import LoginForm from './components/LoginForm';
import Router from './Router';
class App extends Component {
componentWillMount(){
const config = {
apiKey: "AIzaSyDV_vLg656D36E8T9GVraA6ZmZrcUi2QH4",
authDomain: "XXXXXXXXXX.firebaseapp.com",
databaseURL: "https://XXXXXXXXX.firebaseio.com",
projectId: "manager-ba44d",
storageBucket: "manager-ba44d.appspot.com",
messagingSenderId: "200262066369"
};
firebase.initializeApp(config);
}
render() {
const store= createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<Router />
</Provider>
);
}
}
export default App;
my login component that calls the Asynch function:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
import { emailChanged, passwordChanged, loginUser } from '../actions';
import { Card, CardSection, Input, Button, Spinner } from './common';
class LoginForm extends Component {
static navigationOptions = () => ({
title: 'Login screen',
headerTitleStyle: {
textAlign:"center",
flex:1
}
});
onEmailChange(text){
this.props.emailChanged(text);
}
onPasswordChange(text){
this.props.passwordChanged(text);
}
onButtonPress(){
const {email, password, navigation} = this.props;
this.props.loginUser({email, password}, navigation);
}
renderError(){
if(this.props.error){
return(
<View style={{backgroundColor: 'white'}}>
<Text style={styles.errorTextStyle}>
{this.props.error}
</Text>
</View>
)
}
}
renderButton(){
if(this.props.loading){
return <Spinner size="large" />;
}
return (
<Button
style={styles.buttonStyle}
buttonText="Login" onPress={this.onButtonPress.bind(this)}>
</Button>
);
}
render(){
return(
<Card>
<CardSection>
<Input
label="Email"
placeholder="email#gmail.com"
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
/>
</CardSection>
<CardSection>
<Input
secureTextEntry
label="password"
placeholder="password"
onChangeText={this.onPasswordChange.bind(this)}
value={this.props.password}
/>
</CardSection>
{this.renderError()}
<CardSection>
{this.renderButton()}
</CardSection>
</Card>
);
}
}
const mapStateToProps = state => {
return {
email: state.auth.email,
password: state.auth.password,
error: state.auth.error,
loading: state.auth.loading
};
};
export default connect(mapStateToProps,
{
emailChanged,
passwordChanged,
loginUser
})(LoginForm);
const styles = {
errorTextStyle: {
fontSize: 20,
alignSelf: 'center',
color: 'red'
},
buttonStyle:{
flex: 1,
flexDirection: 'row',
alignSelf: 'stretch'
}
}

UPDATE
You need to import NavigationActions like this
import { NavigationActions } from 'react-navigation'
export const loginUser = ({email, password}, navigation) => {
return (dispatch) => {
//indicate start user log in process
dispatch({type: LOGIN_USER});
firebase.auth().signInWithEmailAndPassword(email,password)
.then(user => loginUserSuccess(dispatch, user))
.then(NavigationActions.navigate('employeeList'))
.catch(() => {
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user, navigation))
.catch(() => loginUserFail(dispatch));
});
};
};
const loginUserFail = (dispatch) => {
dispatch({type: LOGIN_USER_FAIL});
}
const loginUserSuccess = (dispatch, user, navigation) => {
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user,
});
NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'employeeList' })],
}),
};

Related

Rematch for Redux: Setting State to that of the Payload when Signing Up

How do I set state to that of my payload? I would like my global state to have the recent changes, not the payload. Please could someone explain why/how this is happening? Do I need to create another reducer/effect to set the state? I want to set this state as global within the app. I would greatly appreciate it.
I am using the following:
Firebase Firestore
Rematch for Redux
React Native
This is My debugger (image).
Result of the code below.
Here is my code:
Index.js
import { init } from '#rematch/core'
import user from './user'
const models = {
user,
}
const store = init({
models,
})
export default { getState, dispatch } = store
Model.js (User.js)
import firebase from 'firebase';
import db from '../config/firebase'
const user = {
state: {},
reducers: {
login(userData) {
return userData
},
email(state, email) {
return { ...state, email }
},
password(state, password) {
return { ...state, password }
},
username(state, username) {
return { ...state, username }
},
fullname(state, fullname) {
return { ...state, fullname }
},
},
effects: () => ({
async signup() {
const { email, password, username, fullname } = getState().user
const response = await firebase.auth().createUserWithEmailAndPassword(email, password)
if (response.user.uid) {
const userData = {
uid: response.user.uid,
email: email,
username: username,
fullname: fullname,
bio: 'test',
gender: 'teste',
phoneNum: 'teste',
profilePic: 'te',
status: 'teste',
}
db.collection('users').doc(response.user.uid).set(userData)
alert(userData.uid)
return dispatch.user.login(userData)
}
}
})
}
export default user
SignUp.js
import * as React from 'react';
import {
TextInput,
Text,
KeyboardAvoidingView,
SafeAreaView,
TouchableOpacity,
Alert,
}
from 'react-native';
import styles from '../styles'
import { connect } from 'react-redux';
import '#expo/vector-icons';
import 'redux';
class Signup extends React.Component {
onPress = () => {
this.props.SignUp()
this.props.navigation.navigate('Home')
}
render() {
const { routeName } = this.props.navigation.state
return (
<SafeAreaView style={styles.container}>
<KeyboardAvoidingView behavior='position'>
<Text style={styles.mainText}>
EMAIL
</Text>
<TextInput
style={styles.inputText}
editable={routeName === 'Signup' ? true : false}
value={this.props.user.email}
onChangeText={input => this.props.setEmail(input)}
/>
<Text style={styles.mainText}>
PASSWORD
</Text>
<TextInput
style={styles.inputText}
editable={routeName === 'Signup' ? true : false}
value={this.props.user.password}
onChangeText={input => this.props.setPassword(input)}
secureTextEntry={true}
/>
<Text style={styles.mainText}>
USERNAME
</Text>
<TextInput
style={styles.inputText}
value={this.props.user.username}
onChangeText={input => this.props.setUserName(input)}
/>
<Text style={styles.mainText}>
FULL NAME
</Text>
<TextInput
style={styles.inputText}
value={this.props.user.fullname}
onChangeText={input => this.props.setFullName(input)}
/>
<TouchableOpacity
style={styles.buttonLighGray}
onPress={() => this.onPress()}>
<Text style={styles.buttonDarkText}>
Accept & Sign Up
</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
}
const mapState = (state) => ({
user: state.user,
})
const mapDispatch = (dispatch) => ({
setEmail: mail => dispatch.user.email(mail),
setPassword: pass => dispatch.user.password(pass),
setUserName: usern => dispatch.user.username(usern),
setFullName: fulln => dispatch.user.fullname(fulln),
SignUp: () => dispatch.user.signup(),
})
export default connect(mapState, mapDispatch)(Signup)
Screen.js
import * as React from 'react';
import {
View,
TextInput,
Alert,
Text,
KeyboardAvoidingView,
SafeAreaView,
TouchableOpacity,
}
from 'react-native';
import styles from '../styles'
import { connect } from 'react-redux';
import { Image } from 'react-native-elements';
import '#expo/vector-icons';
import 'redux';
import firebase from 'firebase'
class Screen extends React.Component {
render() {
return (
<SafeAreaView style={styles.container}>
<Text> Full Name: {this.props.user.fullName}</Text>
<Text> Email: {this.props.user.email}</Text>
<Text> username: {this.props.user.username}</Text>
<Text> bio: {this.props.user.bio}</Text>
<Text> gender: {this.props.user.gender}</Text>
<Text> phoneNum: {this.props.user.phoneNum}</Text>
<Text> profilePic: {this.props.user.profilePic}</Text>
</SafeAreaView>
);
}
}
const mapState = (state) => ({
user: state.user,
})
const mapDispatch = (dispatch) => ({
setEmail: mail => dispatch.user.email(mail),
setPassword: pass => dispatch.user.password(pass),
setUserName: usern => dispatch.user.username(usern),
setFullName: fulln => dispatch.user.fullname(fulln),
})
export default connect(mapState, mapDispatch)(Screen)
The problem is that you are returning the current state again in the login reducer. (you declared is at user data)
login(state, payload) {
return {
...state,
...payload
}
// this will take the global state and overwrite everything that is in payload (merge both objects
},
else you could just do just return payload but this could overwrite other stored values in the future!

The email address is badly formatted - react native, firebase and redux

I'm trying to login using firebase and redux in react native. But It is giving error "The email address is badly formatted." If I dismiss the error and still give email and password, it gives error saying "The password is invalid or the user does not have a password". What might be the problem here?
If the firebase part is removed, the app works perfectly. The inputs works as usual.
Have a look at the video here
Code:
App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reduxThunk from 'redux-thunk';
import reducers from './src/reducers';
import LoginForm from './src/components/LoginForm';
import firebase from '#firebase/app';
import '#firebase/auth';
export default class App extends Component<Props> {
componentWillMount() {
const config = {
apiKey: 'xxxxxxxxxxxxxxx',
authDomain: 'xxxxxxxxxxxxxxx',
databaseURL: 'xxxxxxxxxxxxxxx',
storageBucket: 'xxxxxxxxxxxxxxx',
messagingSenderId: 'xxxxxxxxxxxxxxx'
};
firebase.initializeApp(config);
}
render() {
return (
<Provider store={createStore(reducers, {}, applyMiddleware(reduxThunk))}>
<LoginForm />
</Provider>
);
}
}
Action creators
import firebase from 'firebase';
import { EMAIL_CHANGED, PASSWORD_CHANGED, LOGIN_USER_SUCCESS } from './types';
export const emailChanged = (text) => {
return {
type: EMAIL_CHANGED,
payload: text
};
};
export const passwordChanged = (text) => {
return {
type: PASSWORD_CHANGED,
payload: text
};
};
export const loginUser = ({ email, password }) => {
return (dispatch) => {
firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => {
dispatch({ type: 'LOGIN_USER_SUCCESS', payload: user });
});
};
};
LoginForm.js
import React, { Component } from 'react';
import { } from 'react-native';
import { connect } from 'react-redux';
import { Input, Button, Card, CardSection } from './common';
import { emailChanged, passwordChanged, loginUser } from '../actions';
import firebase from '#firebase/app';
import '#firebase/auth';
type Props = {};
class LoginForm extends Component<Props> {
onEmailChangeText = (text) => {
this.props.emailChanged(text);
}
onPasswordChangeText = (text) => {
this.props.passwordChanged(text);
}
onButtonPress = () => {
const { email, password } = this.props;
this.props.loginUser({ email, password });
}
render() {
return (
<Card>
<CardSection>
<Input
label='Email'
placeholder='email#gmail.com'
onChangeText={(text) => this.onEmailChangeText(text)}
value={this.props.email}
/>
</CardSection>
<CardSection>
<Input
label='Password'
placeholder='Password'
secureTextEntry
onChangeText={(text) => this.onPasswordChangeText(text)}
value={this.props.password}
/>
</CardSection>
<CardSection>
<Button onPress={this.onButtonPress()}>
Login
</Button>
</CardSection>
</Card>
);
}
}
const mapStateToProps = (state) => {
console.log('mapStateToProps', state.auth);
return { email: state.auth.email, password: state.auth.password, delete: state.auth.delete };
};
export default connect(mapStateToProps,
{ emailChanged, passwordChanged, loginUser })(LoginForm);
Reducer:
import { EMAIL_CHANGED, PASSWORD_CHANGED } from '../actions/types';
const INITIAL_STATE = { email: '', password: '', delete: '' };
export default (state = INITIAL_STATE, action) => {
console.log('emailReducer', state);
switch (action.type) {
case EMAIL_CHANGED:
return { ...state, email: action.payload };
case PASSWORD_CHANGED:
return { ...state, password: action.payload };
default:
return state;
}
};

Redux is not getting any data

I'm new to Redux. It's really confusing to understand basic syntax. None of the bugs are found so It's hard to figure out what's wrong with my code.
It worked well last week, I don't remember what I have changed.
//child component
import React, { Component } from 'react';
import { SingleDatePicker } from 'react-dates';
import moment from 'moment';
class InputForms extends Component {
state = {
inputs: ['input-0'],
title: '',
tag: '',
createdAt: moment(),
imageLinks: [''],
calendarFocused: false,
error: '',
}
appendInput(e) {
const newInput = `input-${this.state.inputs.length}`;
this.setState({ inputs: this.state.inputs.concat([newInput]) });
}
onTitleChange = (e) => {
const title = e.target.value;
this.setState(() => ({ title }));
};
onTagChange = (e) => {
const tag = e.target.value;
this.setState(() => ({ tag }));
};
onImageLinkChange = (e) => {
const imageLinks = e.target.value;
this.setState(() => ({ imageLinks: this.state.imageLinks.concat([imageLinks]) }));
};
onDateChange = (createdAt) => {
if (createdAt) {
this.setState(() => ({ createdAt }));
}
};
onFocusChange = ({ focused }) => {
this.setState(() => ({ calendarFocused: focused }));
};
onSubmit = (e) => {
e.preventDefault();
if (!this.state.title || !this.state.imageLinks) {
this.setState(() => ({ error: '제목과 이미지링크를 입력해주세요' }));
} else {
this.setState(() => ({ error: '' }));
this.props.onSubmit({
title: this.state.title,
tag: this.state.tag,
createdAt: this.state.createdAt.valueOf(),
imageLinks: this.state.imageLinks,
});
}
}
render() {
return (
<div>
<form onSubmit={this.onSubmit}>
<input
type="text"
placeholder="제목을 입력하세요"
required
value={this.state.title}
onChange={this.onTitleChange}
/>
<input
type="text"
placeholder="태그를 입력하세요"
value={this.state.tag}
onChange={this.onTagChange}
/>
<SingleDatePicker
date={this.state.createdAt}
onDateChange={this.onDateChange}
focused={this.state.calendarFocused}
onFocusChange={this.onFocusChange}
numberOfMonths={1}
isOutsideRange={() => false}
/>
{this.state.inputs.map((input, key) => {
return <input
key={input}
type="text"
required
value={this.state.imageLinks}
onChange={this.onImageLinkChange}
placeholder={`이미지링크 ${key + 1}`}
/>
})}
<button>저장</button>
</form>
</div>
)
}
}
export default InputForms;
//parent component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import configureStore from '../store/configureStore';
import InputForms from './InputForms';
import { addPost } from '../actions/posts';
const store = configureStore();
class CreatePost extends Component {
onSubmit = (post) => {
this.props.addPost(post);
this.props.history.push('/');
};
render(){
return(
<div>
<InputForms onSubmit={this.onSubmit}/>
</div>
)
}
}
const mapDispatchToProps = (dispatch, props) => ({
addPost: (post) => dispatch(addPost(post))
});
export default connect(undefined, mapDispatchToProps)(CreatePost);
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from './store/configureStore';
import registerServiceWorker from './registerServiceWorker';
import AppRouter from './routers/AppRouter';
import 'normalize.css/normalize.css';
import './style/style.css';
import 'react-dates/lib/css/_datepicker.css';
import 'react-dates/initialize';
const store = configureStore();
const jsx = (
<Provider store={store}>
<AppRouter />
</Provider>
);
ReactDOM.render(jsx, document.getElementById('root'));
registerServiceWorker();
//action
import database from '../firebase/firebase';
//Add Posts
export const addPost = (post) => ({
type: 'ADD_POST',
post
});
//reducer
const postReducerDefaultState = [];
export default (state = postReducerDefaultState, action) => {
switch (action.type) {
case 'ADD_POST':
return [
...state,
action.post
];
default:
return state;
}
};
In your reducer, you return as below
return [ ...state, action.post];
Reducer doesnt return array, but instead returning objects. Secondly, action.post is a value, you need to assign this to key, something like below:
return { ...state, post: action.post };

signinwithemailandpassword failed first argument email must be a valid string

I'm recently practicing React Native with Redux and developing a login form attached to Firebase. The thing is, whenever the signin button is clicked it should give an error above button "authentication failed".
But whenever I tried to click the button, it gives an error: "signinwithemailandpassword failed first argument email must be a valid string".
I've tried hours to solve this but couldn't get the answer.
Here is my code:
LOGINFORM:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { CardSection, Card, Input, Button } from './common';
import { emailChanged, passwordChanged, loginUser } from '../actions';
import { connect } from 'react-redux';
class LoginForm extends Component {
onEmailChange(text) {
this.props.emailChanged(text);
//Its like setState to action, the text is send as a parameter to our action.
}
onPasswordChange(text){
this.props.passwordChanged(text);
}
onButtonPress(){
this.props.loginUser( this.props.email, this.props.password );
}
renderError(){
if (this.props.error){
<View style={{ backgroundColor: 'white' }}>
<Text style={ Styles.errorTextStyle }>
{this.props.error}
</Text>
</View>
}
}
render() {
return(
<Card>
<CardSection>
<Input
label='Email'
placeHolder='email#gmail.com'
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
//What is this function bind to?
//Actually, this bind function has a 'this' keyword and 'this' keyword is actually a
//parameter that is send to the onEmailChange, 'this' is recieved by 'text' param as the
//text written in the email field so its binded and called whenever user writes anything
//in the field of email.
//We're using 'this' onEmailChange is a call me back (callback) function that will invoke
//when input is pressed or not pressed, that is why we're using bind.
/>
</CardSection>
<CardSection>
<Input
secureTextEntry
label='Password'
placeHolder='password'
onChangeText={this.onPasswordChange.bind(this)}
value={this.props.password}
/>
</CardSection>
{this.renderError()}
<CardSection>
<Button onPress={this.onButtonPress.bind(this)}> Login </Button>
</CardSection>
</Card>
);
}
}
const Styles = {
errorTextStyle: {
fontSize: 20,
alignSelf: 'center',
color: 'red'
}
}
const mapStateToProps = (state) => {
return {
email: state.auth.email,
password: state.auth.password,
error: state.auth.error
};
console.log(state.auth.email);
};
export default connect(mapStateToProps, { emailChanged, passwordChanged, loginUser })(LoginForm);
//since we connect and added { emailChanged } as action, now we can access this.props.emailChanged.
ACTION INDEX FILE:
import firebase from 'firebase';
import { EMAIL_CHANGED, PASSWORD_CHANGED, LOGIN_USER_SUCCESS, LOGIN_USER_FAIL } from './types';
export const emailChanged = (text) => {
return{
type: EMAIL_CHANGED,
payload: text
};
}
export const passwordChanged = (text) => {
return {
type: PASSWORD_CHANGED,
payload: text
};
}
export const loginUser = ({ email, password }) => {
return(dispatch) => {
firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch(() => {
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch(() => loginUserFail(dispatch));
});
};
};
const loginUserFail = (dispatch) => {
dispatch({ type: LOGIN_USER_FAIL });
};
const loginUserSuccess = (dispatch, user) => {
dispatch({ type: LOGIN_USER_SUCCESS, payload: user });
};
AUTH REDUCER:
import { EMAIL_CHANGED, PASSWORD_CHANGED, LOGIN_USER_SUCCESS, LOGIN_USER_FAIL } from '../actions/types';
const INITIAL_STATE = { email: '', password: '', user: null, error: '' };
export default (state = INITIAL_STATE, action) => {
//switch statement in Reducer
switch (action.type) {
case EMAIL_CHANGED:
return { ...state, email: action.payload };
case PASSWORD_CHANGED:
return { ...state, password: action.payload };
case LOGIN_USER_SUCCESS:
return { ...state, user: action.payload };
case LOGIN_USER_FAIL:
return { ...state, error: 'Authentication failed.' };
default:
return state;
}
};
APP.JS
import React, {Component} from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import firebase from 'firebase';
import ReduxThunk from 'redux-thunk';
import reducers from './reducers';
import LoginForm from './components/LoginForm'
class App extends Component {
componentWillMount() {
var config = {
apiKey: 'AIzaSyAjHwCvftP1w0nKJTylUQcXAH-rThhZ6sQ',
authDomain: 'bold-circuit-429.firebaseapp.com',
databaseURL: 'https://bold-circuit-429.firebaseio.com',
projectId: 'bold-circuit-429',
storageBucket: 'bold-circuit-429.appspot.com',
messagingSenderId: '270696683683'
};
firebase.initializeApp(config);
}
render(){
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return(
<Provider store={store}>
<LoginForm />
</Provider>
);
}
}
export default App;
It looks like the problem is your connect() function call in LOGINFORM.
I pull out your { emailChanged, passwordChanged, loginUser } argument you're passing to connect() and change it to this:
const mapStateToProps = (state) => {
return {
email: state.auth.email,
password: state.auth.password,
error: state.auth.error
};
console.log(state.auth.email);
};
const mapDispatchToProps = (dispatch) => {
return {
emailChanged: (emailAddress) => dispatch(emailChanged(emailAddress),
passwordChanged: (password) => dispatch(passwordChanged(password)),
loginUser: (email, password) => dispatch(loginUser(email, password))
}
};
export default connect(mapStateToProps, mapDispatchToProps)(LoginForm);
Your initial attempt used object decomposition to create an object to pass to connect. That object ended up looking like
{
emailChanged: emailChanged,
passwordChanged: passwordChanged,
loginUser: loginUser
}
My function will take a dispatch argument and map those three values of props (emailChanged, passwordChanged, loginUser) to functions that dispatch the action to your reducers.
EDIT: Your full LOGINFORM.js file should look like this:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { CardSection, Card, Input, Button } from './common';
import { emailChanged, passwordChanged, loginUser } from '../actions';
import { connect } from 'react-redux';
class LoginForm extends Component {
onEmailChange(text) {
this.props.emailChanged(text);
//Its like setState to action, the text is send as a parameter to our action.
}
onPasswordChange(text){
this.props.passwordChanged(text);
}
onButtonPress(){
this.props.loginUser( this.props.email, this.props.password );
}
renderError(){
if (this.props.error){
<View style={{ backgroundColor: 'white' }}>
<Text style={ Styles.errorTextStyle }>
{this.props.error}
</Text>
</View>
}
}
render() {
return(
<Card>
<CardSection>
<Input
label='Email'
placeHolder='email#gmail.com'
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
//What is this function bind to?
//Actually, this bind function has a 'this' keyword and 'this' keyword is actually a
//parameter that is send to the onEmailChange, 'this' is recieved by 'text' param as the
//text written in the email field so its binded and called whenever user writes anything
//in the field of email.
//We're using 'this' onEmailChange is a call me back (callback) function that will invoke
//when input is pressed or not pressed, that is why we're using bind.
/>
</CardSection>
<CardSection>
<Input
secureTextEntry
label='Password'
placeHolder='password'
onChangeText={this.onPasswordChange.bind(this)}
value={this.props.password}
/>
</CardSection>
{this.renderError()}
<CardSection>
<Button onPress={this.onButtonPress.bind(this)}> Login </Button>
</CardSection>
</Card>
);
}
}
const Styles = {
errorTextStyle: {
fontSize: 20,
alignSelf: 'center',
color: 'red'
}
}
const mapStateToProps = (state) => {
return {
email: state.auth.email,
password: state.auth.password,
error: state.auth.error
};
console.log(state.auth.email);
};
const mapDispatchToProps = (dispatch) => {
return {
emailChanged: (emailAddress) => dispatch(emailChanged(emailAddress),
passwordChanged: (password) => dispatch(passwordChanged(password)),
loginUser: (email, password) => dispatch(loginUser(email, password))
}
};
export default connect(mapStateToProps, mapDispatchToProps)(LoginForm);
//since we connect and added { emailChanged } as action, now we can access this.props.emailChanged.
onButtonPress(){
this.props.loginUser( this.props.email, this.props.password );
}
wrong call LoginUser function
onButtonPress(){
this.props.loginUser({ this.props.email, this.props.password });
}

authentication is not working with firebase using react-native-redux + redux-thunk

the code is working and app also showing on mobile but the problem is that the authentication is always failed even i entered the correct email/pass.
this authAction.js file.
import firebase from 'firebase';
import { LOGIN_USER_FAIL, EMAIL_CHANGED , PASSWORD_CHANGED , LOGIN_USER_SUCCESS, LOGIN_USER } from './types';
import { Actions } from 'react-native-router-flux';
//here define the actionCreator of email
export const emailChanged = (text) => {
return {
//type of action defined in type.js file
type: 'EMAIL_CHANGED',
payload: text
};
};
//here define the actionCreator of password
export const passwordChanged = (text) => {
return {
//type of action defined in type.js file
type: 'PASSWORD_CHANGED',
payload: text
};
};
export const loginUser = ({ email, password}) => {
return (dispatch) => {
dispatch({type: LOGIN_USER});
firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch( () => {
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user) )
.catch(() => loginUserFail(dispatch));
});
};
};
const loginUserFail = (dispatch) => {
dispatch({type: LOGIN_USER_FAIL});
Actions.main();
};
const loginUserSuccess = ( dispatch, user) => {
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user
});
};
this is types.js file where i stored all the strings in variables
export const EMAIL_CHANGED = 'email_changed';
export const PASSWORD_CHANGED = 'password_changed';
export const LOGIN_USER_SUCCESS = 'login_user_success';
export const LOGIN_USER_FAIL = 'login_user_fail';
export const LOGIN_USER = 'login_user';
this is authReducer.js file
import { EMAIL_CHANGED , PASSWORD_CHANGED, LOGIN_USER_SUCCESS, LOGIN_USER_FAIL, LOGIN_USER } from '../actions/types';
const INITIAL_STATE = { email: '', password: '', user: null, error: '', loading: false };
export default (state= INITIAL_STATE, action) => {
switch(action.type){
//..state isto update the state object
case EMAIL_CHANGED:
return { ...state, email: action.payload};
case PASSWORD_CHANGED:
return { ...state, password: action.payload };
case LOGIN_USER_SUCCESS:
return { ...state, user : action.payload, error:'', loading: false};
case LOGIN_USER_FAIL:
return { ...state, error: 'Authentication Failed', password: '', loading: false };
case LOGIN_USER:
return { ...state, loading: true, error: '' };
default:
return state;
}
};
this is app.js file where i inserted the firebase API
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { createStore, applyMiddleware } from 'redux';
import firebase from 'firebase'
import rootReducer from './reducers';
import LoginForm from './components/LoginForm';
class App extends Component {
componentWillMount() {
firebase.initializeApp({
apiKey: 'AIzaSyAe45P2xc8q09gpEV6oA6s4U9YjnaTf9vc',
authDomain: 'manager-6cde7.firebaseapp.com',
databaseURL: 'https://manager-6cde7.firebaseio.com',
projectId: 'manager-6cde7',
storageBucket: 'manager-6cde7.appspot.com',
messagingSenderId: '408722531799'
});
}
render()
{
const store = createStore(rootReducer,applyMiddleware(thunk));
return(
<Provider store={store} >
<LoginForm />
</Provider>
);
}
}
export default App;
this is loginForm.js file.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { emailChanged, passwordChanged, loginUser } from '../actions';
import { Button, Card, CardSection, Input, Spinner } from './common';
import { Text } from 'react-native';
class LoginForm extends Component {
onEmailChange(text){
//now call the actionCreator in actions index.js
this.props.emailChanged(text);
}
onPasswordChanged(text){
//now call the actionCreator in actions index.js
this.props.passwordChanged(text);
}
onButtonPress() {
const {email, password} = this.props;
this.props.loginUser({email, password});
}
renderButton(){
if(this.props.loading)
{
return <Spinner size="large" />;
}
return (
<Button onPress={this.onButtonPress.bind(this)} >LogIn</Button>
);
}
render(){
return(
<Card>
<CardSection>
<Input
onChangeText={this.onEmailChange.bind(this)}
label="Email"
placeholder="email#gmail.com"
value={this.props.email}
/>
</CardSection>
<CardSection>
<Input
value={this.props.password}
onChangeText={this.onPasswordChanged.bind(this)}
secureTextEntry
label="Password"
placeholder="******"
/>
</CardSection>
<Text style={styles.errorAuthentication}>{this.props.error}</Text>
<CardSection>
{this.renderButton()}
</CardSection>
</Card>
);
}
}
const mapStateToProps = ({auth}) => {
const { email, password, error, loading } = auth;
return {
email, password, error, loading };
};
const styles = {
errorAuthentication: {
fontSize: 20,
color: 'red',
alignSelf: 'center'
}
};
export default connect(mapStateToProps, { emailChanged, passwordChanged, loginUser })(LoginForm);

Resources