DEV Community

Cover image for Using Vue Composition API with Firebase
Aaron K Saunders
Aaron K Saunders

Posted on • Edited on

Using Vue Composition API with Firebase

Overview

We are going to use the new VueJS Composition API to separate out Firebase integration into a VueJS Application. We will focus in this blog post on determining if there is already user information associated with the application saved or if the user needs to login to the application. If the user needs to login to the application we will provide the ability to login and logout of firebase using the composition api application structure

Video Series Playlist

https://www.youtube.com/playlist?list=PL2PY2-9rsgl2bgNTX9omlDisiWh1NYInz

We are assuming there is a basic understanding of VueJS and Firebase. Addition information on setting up you application to use the Firebase Javascript SDK can be found at this link

Getting Started

Please take a look at my video here VueJS Composition API Sample App w/video to get started with understanding the VueJS Composition API if you are not already familiar

Setting Up main.js

Make sure you install the Vue2 plugin for the Composition API

import Vue from 'vue'
import App from './App.vue'
import VueCompositionApi from "@vue/composition-api";


Vue.config.productionTip = false
Vue.use(VueCompositionApi);



new Vue({
  render: h => h(App),
}).$mount('#app')
Enter fullscreen mode Exit fullscreen mode

In our App.vue we have a basic template that displays a list of things using the ThingList component, and we display a form using the Login Component.

What is rendered is controlled by three reactive properties loading, error and user.

<template>
  <div id="app">
    <div v-if="loading">LOADING...</div>
    <div v-else-if="error">{{error}}</div>
    <div v-else-if="user">
      <h2>{{user.displayName}}&nbsp;&nbsp;{{user.email}}</h2>
      <h4>
        <button @click="logout()">LOGOUT</button>
      </h4>
      <hr />
      <br />
      <ThingList />
    </div>
    <div v-else>
      <LoginForm></LoginForm>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

The properties are derived from the use of vuejs new composition api that has been announced for 3.0 but is accessible in 2.0 by using a vue-composition-api plugin

Vue2 plugin for the Composition API. https://buff.ly/35TgSja

Vue Composition API Function: useAuth

This function will do the firebase authentication check and set the user property or the error property when checking the authentication status of the user. While firebase is making the api call the loading property will also be set.

All we are doing here is making the firebase api call and updating the properties in the function based on the result of the api call. Since these state variables are reactive, as they are updated it will cause the screen/UI be be re-rendered

import { toRefs, reactive } from "@vue/composition-api";
import firebase from "firebase";
// Required for side-effects
import "firebase/firestore";

// initialize firebase, this is directly from the firebase documentation
// regarding getting started for the web
if (firebase.apps.length === 0) {
  const firebaseConfig = {
    /* YOUR CONFIGURATION GOES HERE */
  };
  firebase.initializeApp(firebaseConfig);
}

export default function() {
  // our reactive properties...
  let state = reactive({
    user: null,
    loading: true,
    error: null
  });

  // make the firebase call to listen for change in auth state,
  // we have set initial loading status to true so nothing happens on UI 
  // side until loading is set to false
  firebase.auth().onAuthStateChanged(_user => {
    if (_user) {
      state.user = _user;
    } else {
      state.user = null;
    }
    state.loading = false;
  });

  // return all of the properties from the function
  return {
    ...toRefs(state)
  };
}

Enter fullscreen mode Exit fullscreen mode

Vue Composition API Function: useLogin

This function will do the firebase login and logout functionality; this will set the user property or the error property when checking the authentication status of the user. While firebase is making the api call the loading property will also be set.

Important to note that when we make login call and set error if it happens, we don't need to set the user object because the other composition function is listening for change in user state and when detected it will update the user object

import { toRefs, reactive, computed } from "@vue/composition-api";
import firebase from "firebase";
// Required for side-effects
import "firebase/firestore";

export default function() {
  let state = reactive({
    error: null,
    username: null,
    password: null,
    user: null
  });

  /**
  * have this value `isValid` get updated when the dependent properties 
  * are changed in the composition function
  */
  let isValid = computed(() => {
    let { username, password } = state;
    return (
      username !== null &&
      username.length !== 0 &&
      password !== null &&
      password.length !== 0
    );
  });


  const login = () => {
    firebase
      .auth()
      .signInWithEmailAndPassword(state.username, state.password)
      .then(() => {}, error => (state.error = error))
      .catch(error => {
        // Handle Errors here.
        state.error = error;
      });
  };

  const logout = () => {
    firebase
      .auth()
      .signOut()
      .then(() => {}, error => (state.error = error))
      .catch(error => {
        // Handle Errors here.
        state.error = error;
      });
  };

  return {
    // return all of the properties from the function
    ...toRefs(state),

    // return all of the computed value to make sure we
    // get non null values for username/password from the 
    // function
    isValid,

    // pass back a login and logout function to be utilized 
    // by the login form
    login,
    logout
  };
}
Enter fullscreen mode Exit fullscreen mode

Returning to App Component

Now that we have all of the composition function set, we can walk through what is really happening in the application.

<script>
import ThingList from "./components/ThingList.vue";
import LoginForm from "./components/LoginForm.vue";

// composition api imports
import { computed } from "@vue/composition-api";

// our custom composition functions for firebase auth check
// and for logging in and logging out of firebase
import useAuth from "./use-auth";
import useLogin from "./use-login";

export default {
  name: "app",
  components: {
    ThingList,
    LoginForm
  },
  setup() {
    // load in the authentication properties
    let { user, loading, error } = useAuth();

    // load in the login function
    let loginState = useLogin();


    return {
      user,
      loading,

      // here we need to ensure that we get the error from 
      // either the loginState or the error returned from the useAuth
      // function
      error : computed(() => (loginState.error  || error).value),

      // set the logout function from the usLogin composition function
      logout: loginState.logout
    };
  }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  padding: 60px;
}
</style>
Enter fullscreen mode Exit fullscreen mode

When we call this function

let { user, loading, error } = useAuth();
Enter fullscreen mode Exit fullscreen mode

we will call firebase to see if there is already user information available from a previous login, if so the user object is set accordingly otherwise we have no user, no error and the application will render the view because the values are reactive and we will be then showing the Login Form

(a) when firebase is checking for user we are showing loading
(b1) when loading is complete do we have an error?
(b2) when loading is complete do we have a user? If so then render the ThingList Component and some user information
(b3) when loading is complete do not we have a user? If so render the Login Form
Enter fullscreen mode Exit fullscreen mode
<template>
  <div id="app">
    (a) <div v-if="loading">LOADING...</div>
    (b1)<div v-else-if="error">{{error}}</div>
    (b2)<div v-else-if="user">
      <h2>{{user.displayName}}&nbsp;&nbsp;{{user.email}}</h2>
      <h4>
        <button @click="logout()">LOGOUT</button>
      </h4>
      <hr />
      <br />
      <ThingList />
    </div>
    (b3)<div v-else>
      <LoginForm></LoginForm>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Login Form

Back to the Login Form to see how this all fits together; when the application loads this component we have access to the useLogin composition function. This function returns as it state object

  • username
  • password
  • error

and returns two functions

  • login
  • logout

and returns a computed function we use to check if fields are valid

  • isValid

After the login method is called, we will get an error which will be displayed on the template, or we will get a successful outcome which will change the state of the useAuth composition function. It will then return a valid user object which would then cause the application to render the ThingList component and hide the LoginForm component.

<template>
  <div>
    <input type="text" placeholder="username" v-model="username" />
    <input type="password" placeholder="password" v-model="password" />
    <button @click="login" :disabled="!isValid">LOGIN</button>
    <p>{{error}}</p>
  </div>
</template>

<script>
// import { reactive, toRefs } from "@vue/composition-api";
import useLogin from "../use-login";
export default {
  setup() {
    let state = useLogin();
    return {
        ...state
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Conclusion

The Vue Composition API is a pretty interesting addition to VueJS 3.0 release and I think it provides similar functionality to react-hooks so vuejs developers should not feel like they are missing out on anything here.

if you find some errors/typos/mistakes or something isn't clear, please leave a comment below.

GitHub logo aaronksaunders / vue-composition-firebase-app

use the new VueJS Composition API to seperate out Firebase integration into a VueJS Application.

PART ONE: VueJS with Composition API and Ionic Framework Components

Overview

We are going to use the new VueJS Composition API to seperate out Firebase integration into a VueJS Application. We will focus in this blog post on determining if there is already user information associated with the application saved or if the user needs to login to the application. If the user needs to login to the application we will provide the ability to login and logout of firebase using the composition api application structure

See complete blog post

Here the source code for Part II

Check Out The Video Series Starting Here:

https://youtu.be/3MLHCRep_MM

See the original project that this application is based on:






About Clearly Innovative

Clearly Innovative is a minority-owned solutions provider that develops digital products. We shape ideas into viable products and transform client needs into enhanced technology solutions. As a leader in early adoption and implementation of cutting edge technologies, Clearly Innovative provides services focused on product strategy, user experience, design and development. According to CEO, Aaron Saunders "We are not just designers and developers, but end-to-end digital solution providers." Clearly Innovative has created a tech education program, Clearly Innovative Education, whose mission is to create a world where people from underrepresented backgrounds can have a seat at the digital table as creators, innovators and entrepreneurs.

#TheFutureIsWrittenInCode

The Future is Written in Code series, as part of Inclusive Innovation Incubator, provides introductory and advanced programming classes as well as coding courses with a focus on business and entrepreneurship. Select programming offered includes Coding, UI/UX, Coding & Business, Coding & Entrepreneurship, Business Canvassing, Entrepreneurship: Developing Your Idea into App, to name a few. Please contact info@in3dc.com to find out more!

Top comments (5)

Collapse
 
razbakov profile image
Aleksey Razbakov

Hi! Thank you for article! It's a nice first step with vue-composition-api and firebase!
Even though there is a problem that useAuth is not reusable.

Let's say you want to have components like TheHeader, Account, UserAvatar where you would have setup function and there call useAuth. Every time you call useAuth you create a new listener onAuthStateChanged. It will work of course, but there is no single source of the truth anymore.

Here is how I did it, but I am still not sure if it is a right way to implement shared state. Probably we have to use inject and provide for this.

Collapse
 
aaronksaunders profile image
Aaron K Saunders

you really don't need to call onAuthStateChanged since firebase has the current user persisted. I was investigating using overmindjs or vuex to actually manage the state in combination with the composition api

Collapse
 
razbakov profile image
Aleksey Razbakov • Edited

I mean in your use/auth.js

export default function() {
  // our reactive properties...
  let state = reactive({
    user: null,
    loading: true,
    error: null
  });

  // make the firebase call to listen for change in auth state,
  // we have set initial loading status to true so nothing happens on UI 
  // side until loading is set to false
  firebase.auth().onAuthStateChanged(_user => {
    if (_user) {
      state.user = _user;
    } else {
      state.user = null;
    }
    state.loading = false;
  });

  // return all of the properties from the function
  return {
    ...toRefs(state)
  };
}

Every time you call const { user } = useAuth() in one of your components it will execute this part:
firebase.auth().onAuthStateChanged....

What if you need to access user in multiple components?

Btw, have you checked my implementation?

Thread Thread
 
paulvanbladel profile image
paul van bladel

completely valid point, the event registration should be executed only once in the app. also the state should be kept outside the hook function, in such a way it is shared over components initiating the hook.
The way how the state is exposed now makes the state mutable outside the hook function. If the state is exposed as a computed, it becomes unmutable, that's cleaner.

Collapse
 
aaronksaunders profile image
Aaron K Saunders • Edited

please check out the recently uploaded video covering this here on @YouTube youtube.com/watch?v=3MLHCRep_MM