Search:   Dictionary All Posts
Store Your Knowledge at the Brain Bank!

Firebase Authentication - User Logins

By Jamie in Lessons / Programming - React  1.28.19  
Summary - Register a new user using Firebase Authentication services in React. Use Firebase to store the user's profile data. Auth handles the password and email validations.
Firebase Auth - (not firestore - firebase) - to register/login/logout users
We do not need to create a database table for users to have an account with their login details. Firebase does this for us. Go to 'Authentication' in your project in Firebase and you will see all the users that exist in your system.

We use specific functions that come with firebase in order to create, login, and logout users.

Creating New Users - Intro...
We first must add them to the firebase authentication area, then, if we want, we can create a new collection in the firestore that will hold that user's data, like their first name, last name, address, stuff like that. The only thing that is stored in the Firebase Authentication is the users email and password used for logging in.

This way, Google's Firebase takes care of securing passwords, authenticating email address, logins, and registering new users.

We will need to use an async method to register a new user because first, we must create a new user in Firebase, and then get that user's new id and store in a user's collection of the Firestore, which will hold user's profile data.

The Redux Action for Creating New Users
export const register = newUser => {
    return (dispatch, getState, { getFirebase,  getFirestore }) => {
        const firebase = getFirebase()
        const firestore = getFirestore()
        
        firebase.auth().createUserWithEmailAndPassword(
            newUser.email,
            newUser.password
        )
            .then(res => {
                return firestore.collection('users').doc(res.user.uid).set({
                    firstName: newUser.firstName,
                    lastName: new User.lastName,
                    initials: newUser.firstName[0] + newUser.lastName[0]
            })
            .then(() => dispatch({ type: REGISTER_SUCCESS })
            .catch(err => dispatch({type: REGISTER_FAILED, err })
}

createUserWithEmailAndPassword - is a firebase provided promise function and the two parameters are simply the form's passed in email and password. When that is done we get the response and pass it to the .then()

firestore.collection('users') - this will create that collection if it doesn't exist, so there is no need to have this collection previously setup in the firestore admin.   

we use .doc() - as opposed to - .add() because .add() will automatically create a new document id, but we don't want to set the document id to a random value, we want it to be equal to the id we just created, so we set it equal to the response, which has an object called 'user' and a property called 'uid'. That object and its properties are set by firebase.

then we use .set() to set an object with the properties we want and the values from the submitted registration form. 

All that was another promise, in which we need to get a response and send the thunk dispatch function to the reducers, which will update our state with the new user... and then of course we catch an error if there were any

Dispatch the Action from the Appropriate Page

import { register } from '../../store/actions/authActions'

...component here...
... in the component there will be a form ...
... onSubmit={onSubmit} ...
... the onSubmit function will e.preventDefault(), then run the register(this.state)...
... this.props.register(this.state) ...
... the form should have been handled as local state on the register page ...
... as the user adds data to the form the local state is updated ...

const mapDispatchToProps = dispatch => {
    return {
        register: newUser => dispatch(register(newUser))
    }
}

export default connect(null, mapDispatchToProps)(Component)

Add the Firebase Reducer to rootReducer
import { combineReducers } from 'redux'
import { firestoreReducer } from 'redux-firestore'
import { firebaseReducer } from 'react-redux-firebase'

const rootReducer = combineReducers({
    auth: authReducer,
    project: projectReducer,
    firestore: firestoreReducer,
    firebase: firebaseReducer
})

export default rootReducer

Handle the action in the authReducer.js

switch(action.type){
    case REGISTER_SUCCESS:
        return {
            ...state,
            authError: null
        }
    case REGISTER_ERROR
        return {
            ...state,
            authError: action.err.message
        }
    default:
        return state




Syncing User Data with Profile Data via Firebase

This is easier to do all kinds of calls. For instance, we don't 'get id from user' then get profile of user with id===user id. Instead, firebase has a built in method for syncing up a logged in user's information within the firebase store by getting the data from a specified firestore collection. Like this...

index.js - in the reactReduxFirebase() function in the store compose method, add the following properties to the second argument...

reactReduxFirebase(fbconfig, { userFireStoreForProfile: true, userProfile: 'users', attachAuthIsReady: true })

attachAuthIsReady is another property we add for a different reason. We want to run ReactDOM.render() only after firebase checks to see if the user is logged in. This prevents the DOM from loading with data for non-logged in users, only to re-re-render with data for logged in users a second later. Instead, it simply waits to check if the user is logged in, then loads the appropriate data. by adding the property above attachAuthIsRead: true, firebase gives us a (async) promise function which we can use to wrap the ReactDOM.render() function as so...

store.firebaseAuthIsReady.then(() => {
    ReactDOM.render(<App/>, document.getElementById('root'))
})