loading...

Working With ReactJS and OvermindJS - Integrating Firebase for Data Storage

aaronksaunders profile image Aaron K Saunders ・4 min read

Using OvermindJS (4 Part Series)

1) A Quick Intro To OvermindJS In ReactJS 2) User Login In React JS with OvermindJS 3) Working With ReactJS and OvermindJS - Create User Account 4) Working With ReactJS and OvermindJS - Integrating Firebase for Data Storage

We have been working through a simple authentication and create account flow using OvermindJS and ReactJS, using Ionic Framework React Components for the User Interface. The application covers Create Account, Save User Information, Login & Logout.

We are currently tracking the user information and the authentication status in the OvermindJS State, without managing the interaction with a real data backend which is what we are covering here. We will add a firebase api to the application and the integration point will be through effects.ts

Initialization

First for initialization, we can revisit our onInitialize function in overmind/index.ts. The call remains the same, but we will integrate firebase initialization to set the state and get the current user if necessary.

// overmind/index.ts
export const onInitialize: OnInitialize = async (
  { state, effects },
  overmind
) => {
  let user = await effects.initialize();
  state.currentUser = user;
  state.initialized = true;
};

TheonInitialize is used for setting the information based on the result from the effects call to firebase API.

// effects.ts
export const initialize = () => {
  return firebaseAPI.authInit();
};

// firebase-data.ts
export const authInit = () => {
  return new Promise((resolve, reject) => {
    const unsubscribe = firebaseApp
      .auth()
      .onAuthStateChanged((firebaseUser) => {
        unsubscribe();
        resolve(firebaseUser);
        return;
      });
  });
};

Create User

type UserInfo = {
  email: string;
  firstName: string;
  lastName: string;
  password: string;
  uid?:string
}

Overmind also provides a functional API to help you manage complex logic. This API is inspired by asynchronous flow libraries like RxJS, though it is designed to manage application state and effects.

Using the more functional approach to creating a user, we get introduced to operators.

Using various operators, piped together and checking for errors we can create the user, and additional user data while properly managing errors.

We are using pipe and catchError in this example today.

We want to executed the firebase api call to create the user and then store additional data in a new record in the user collection.

// actions.ts
export const createAccountAndUserRecord: Operator<void, any> = pipe(
  mutate(async ({ state, effects }, userInfo: any) => {
    state.error = null;
    state.currentUser = null;
    let { email, password } = userInfo;
    let newUserInfo = await effects.createAccount({ email, password });
    state.currentUser = { ...newUserInfo.user, uid: newUserInfo?.user?.uid };
  }),
  mutate(
    async ({ state, effects }, userInfo: any) => {
      let newUserInfo = await effects.createUserRecord(userInfo);
      state.currentUser = { ...newUserInfo?.data() };
    }
  ),
  catchError(({ state }, error: Error): Operator<Error, never> => {
    state.error = error;
    throw new Error(error.message);
  })
);

Looking into the associated effects.ts file where we call the APIs in the firebase-data.ts file

// effects.ts - create user
export const createAccount = async (userInfo: {
  email: string;
  password: string;
}) => {
  return await firebaseAPI.createAccount(userInfo);
};

// firebase-data.ts - firebase create user
export const createAccount = ({ email, password }:{
  email: string;
  password: string;
}) => {
  return firebaseApp.auth()
          .createUserWithEmailAndPassword(email, password);
};

Now we need to create the entry in the user collection by calling effects.createUserRecord

// effects.ts - create user record
export const createUserRecord = async (userInfo: {
  email: string;
  firstName: string;
  lastName: string;
  uid: string;
}) => {
  return await firebaseAPI.createUserRecord(userInfo);
};

// firebase-data.ts  - firebase create user record
export const createUserRecord = async (info: {
  email: string;
  firstName: string;
  lastName: string;
  uid: string;
}) => {
  let usersRef = firebase.firestore().collection("users").doc(info.uid);
  let created = firebase.firestore.Timestamp.fromDate(new Date());

  let newUserData = {
    ...info,
    created,
  };

  await usersRef.set(newUserData);

  return (await usersRef.get()).data();

};

In the file firebase-data.ts where we create user record by adding it to the users collection; you can see in the end where we are querying the record again.

return (await usersRef.get()).data();

That is because we want the user record with all of the data including the timestamp which was generated by the firebase server.

Login User

This is pretty straight forward, no a use of operators, just a straight call from actions to effects to firebase api

export const doLogin: Action<any, any> = async (
  { state, effects },
  credentials: { email: string; password: string }
) => {
  try {
    state.error = null;
    state.currentUser = null;

    let { user } = await effects.login(credentials);

    state.currentUser = {
      email: user?.email,
      username: user?.displayName || user?.email,
      uid: user?.uid,
    };
    return state.currentUser;
  } catch (error) {
    state.currentUser = null;
    state.error = error;
    return error;
  }
};

The actions and effects are literally just passing through the credential parameters from one to another.

// effects.ts - login
export const login = async ({
  email,
  password,
}: {
  email: string;
  password: string;
}) => {
  return await firebaseAPI.login(email, password);
};

// firebase-data.ts
export const login = (email: string, password: string) => {
  return firebaseApp.auth().signInWithEmailAndPassword(email, password);
};

Logout User

This is pretty straight forward also, no a use of operators, just a straight call from actions to effects to firebase api

// actions.ts
export const doLogout: AsyncAction<void, boolean> = async ({
  state,
  effects,
}) => {
  state.error = null;
  state.currentUser = null;

  await effects.logout();
  return true;
};

Once again, the actions and effects are literally just passing through the credential parameters from one to another.

// effects.ts
export const logout = async () => {
  return await firebaseAPI.logout();
};

// firebase-data.ts
export const logout = async () => {
  return await firebaseAPI.logout();
};

Conclusion

This is the last piece of the puzzle to create the account and to work through authentication with firebase as the backend database. There certainly are additional optimizations and changes that could be made, but this was meant to be a simple introduction to the concepts.

Please checkout the associated videos on YouTube and the source code in the GitHub Repo.

GitHub logo aaronksaunders / user-login-overmind-react

User Authentication & Account Creation Pattern In ReactJS with Ionic Framework & OvermindJS for state management

user-login-overmind-react

YOUTUBE TUTORIALS COVERING IONIC & REACTJS

#reactjs #javascript #overmindjs

User Authentication Pattern In ReactJS Using OvermindJS

Simple authentication flow using overmindjs and reactjs, using ionic framework components for the UI.

Tracking User Authentication State In ReactJS Using OvermindJS

Setup

The firebase information is stored in an env.js file that needs to be added to your project with your specific credentials

export const FIREBASE_CONFIG =  { 
  [ YOUR CREDENTIALS HERE ]
}

See Tags For Functionality

Associated Links

Using OvermindJS (4 Part Series)

1) A Quick Intro To OvermindJS In ReactJS 2) User Login In React JS with OvermindJS 3) Working With ReactJS and OvermindJS - Create User Account 4) Working With ReactJS and OvermindJS - Integrating Firebase for Data Storage

Posted on Mar 21 by:

aaronksaunders profile

Aaron K Saunders

@aaronksaunders

See more, like and subscribe 👉🏾 ‪Aaron Saunders 📺 https://www.youtube.com/aaronsaundersci?sub_confirmation=1

Discussion

markdown guide