Authentication in React Native using Firebase part 2
In the last part of this tutorial, we created the component for a login display. In this part, we are going to make the login work.
First, we register the project on Firebase.
NB: You need a google account to sign in
Locate and click the Go to console
button on the upper-right corner of the screen.
Click on Add project
Give the name of the project Authenticator
, accept terms and create the project.
In the project dashboard, click on Authentication
Click on set up sign-in method
Click on the authentication method you want for your app. In our case, we chose Email/Password
Click on enable
and save
Go to your development environment, open a terminal and install firebase client
npm install -- save firebase
import firebase to the project. On App.js
add
import firebase from 'firebase'
Then we are going to set up a configuration for the firebase. To get the configuration, we head over to firebase console.
Click on Web setup
and copy the config object.
On our project, go to App.js
and add the code below in the App
class just before the render
method.
componentDidMount() {
let config = {
apiKey: "**********************",
authDomain: "******************",
databaseURL: "*****************",
projectId: "*******************",
storageBucket: "***************",
messagingSenderId: "***********"
};
firebase.initializeApp(config);
}
The code above initializes Firebase with the config we just copied from the Firebase console anytime the app loads. componentDidMount
is a lifecycle method that always runs anytime the app loads.
We will also keep track of the error in the state. Update the constructor
like so
constructor(props) {
super(props);
this.state = { email: '', password: '', error: ''};
}
Next, we add a function to sign the user in or create an account if the user does not exist in the database. In the LoginForm.js
add the following method before the render
method.
onButtonPress() {
this.setState({ error: '', loading: true })
const { email, password } = this.state;
firebase.auth().signInWithEmailAndPassword(email, password)
.then(this.onLoginSuccess.bind(this))
.catch(() => {
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(this.onLoginSuccess.bind(this))
.catch((error) => {
let errorCode = error.code
let errorMessage = error.message;
if (errorCode == 'auth/weak-password') {
this.onLoginFailure.bind(this)('Weak password!')
} else {
this.onLoginFailure.bind(this)(errorMessage)
}
});
});
} onLoginSuccess() {
this.setState({
email: '', password: '', error: '', loading: false
})
} onLoginFailure(errorMessage) {
this.setState({ error: errorMessage, loading: false })
}
So here is what is going on, when a user signs in, the method firebase.auth().signInWithEmail()
is called, and on a successful sign in, we call the onLoginSuccess
function. However, if the user does not exist in our database, it tries to create a new account for the user by calling the firebase.auth().createUserWithEmailAndPassword()
. When it creates the user account, it calls the onLoginSuccess
else it calls onLoginFailure
with the respective error message.
On the render method, update the Button
tag with onButtonPress
callback function like so
<Button title="Sign in" onPress={this.onButtonPress.bind(this)} />
Anytime the button is clicked, it calls the onButtonPress
When a sign-in is unsuccessful, we display a nice error message. Add the following code to the last part of the render
method before the </View>
tag
<Text style={styles.errorTextStyle}>
{this.state.error}
</Text>
Add the styling
const styles = {
errorTextStyle: {
fontSize: 18,
alignSelf: 'center',
color: 'red'
}
}
We need a nice little spinner to show the app is processing the sign-in. To do that we import the ActivityIndicator
from react-native like so
import { View, Button, Text, ActivityIndicator } from 'react-native';
Then render the spinner when loading
is true. To do this, we create a new function that will render the Button
component and the spinner conditionally.
renderButton() {
if (this.state.loading) {
return(
<View style={styles.spinnerStyle}>
<ActivityIndicator size={"small"} />
</View>
);
} return (
<Button
title="Sign in"
onPress={this.onButtonPress.bind(this)}
/>
);
}
Add the renderButton
in place of the <Button/>
tag. The render
method should look like
render() {
return (
<View>
<Input label="Email"
placeholder="user@mail.com"
value={this.state.email}
secureTextEntry={false}
onChangeText={email => this.setState({ email })}
/> <Input label="Password"
placeholder="password"
value={this.state.password}
secureTextEntry={true}
onChangeText={password => this.setState({ password })}
/> {this.renderButton()}
<Text style={styles.errorTextStyle}>
{this.state.error}
</Text>
</View>
);
}
When you reload the simulator, you should be able to sign in or see a nice error message when the sign-in is unsuccessful.
Now we can sign the user in. But there is more to be done. We need to hide the login form on a successful sign in and display a button for sign out. Let us tweak our code for sign out.
On App.js
we need to monitor the state when a sign in is successful and when it's not. To do that, add a state just before componentDidMount
like so
state = {loggedIn: null };
In the componentDidMount
add the following code after firebase.initializeApp(config)
firebase.auth().onAuthStateChanged((user) => {
if(user){
this.setState({ loggedIn: true})
} else {
this.setState({loggedIn: false})
}
})
The firebase.auth()onAuthStateChanged()
monitors the state of the authentication. If the user is signed in, we set the loggedIn
state to true, if the user signs out, we set state to false. Next, we do a conditional rendering of the login form and update the render method like so
renderComponent() {
if (this.state.loggedIn) {
return (
<Button
title="Sign out"
onPress={() => firebase.auth().signOut()}
/>
);
}
return (
<LoginForm />
);
}
render() {
return (
<View>
<Header title='Authenticator' />
{this.renderComponent()}
</View>
);
}
We added a button to sign out the user after a successful login. When the button is clicked, it calls the firebase.auth().signout()
method which signs out the user.
Your App.js
file should look like this
import React, { Component } from 'react';
import { View, Button} from 'react-native';
import firebase from 'firebase'
import Header from './src/components/Header'
import LoginForm from './src/components/LoginForm'export default class App extends Component {
state = { loggedIn: null };componentDidMount() {
let config = {
apiKey: "**********************",
authDomain: "******************",
databaseURL: "*****************",
projectId: "*******************",
storageBucket: "***************",
messagingSenderId: "***********"
};
firebase.initializeApp(config);
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({ loggedIn: true })
} else {
this.setState({ loggedIn: false })
}
})
}renderComponent() {
if (this.state.loggedIn) {
return (
<Button
title="Sign out"
onPress={() => firebase.auth().signOut()}
/>
);
} else {
return (
<LoginForm />
);
}
} render() {
return (
<View>
<Header title='Authenticator' />
{this.renderComponent()}
</View>
);
}
}
Now when a user signs in, the login form is cleared.
When you sign in with a wrong password, you get an error message
The complete code for this project can be found here
Suggestions, questions and comments are welcome. Thanks for reading.