Working With ReactJS and OvermindJS - Integrating Firebase for Data Storage
Tags: react, javascript, tutorial, webdev
Published: 2020-05-08 15:59:28

We have been working through a simple authentication and create account flow using [OvermindJS](https://overmindjs.org/) and [ReactJS](https://reactjs.org/), using [Ionic Framework React Components](https://ionicframework.com/docs/react) 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.
```typescript
// overmind/index.ts
export const onInitialize: OnInitialize = async (
  { state, effects },
  overmind
) => {
  let user = await effects.initialize();
  state.currentUser = user;
  state.initialized = true;
};
```
The`onInitialize` is used for setting the information based on the result from the `effects` call to firebase API.
```typescript
// 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
```typescript
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. 
- https://overmindjs.org/core/going-functional


Using the more [functional approach](https://overmindjs.org/core/going-functional) 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](https://overmindjs.org/api-1/operators#pipe) and [catchError](https://overmindjs.org/api-1/operators#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.

```typescript
// 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
```typescript
// 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`
```typescript
// 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.
```typescript
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

```typescript
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.
```typescript
// 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
```typescript
// 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.
```typescript
// 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 aaronksaunders/user-login-overmind-react %}