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')
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}} {{user.email}}</h2>
<h4>
<button @click="logout()">LOGOUT</button>
</h4>
<hr />
<br />
<ThingList />
</div>
<div v-else>
<LoginForm></LoginForm>
</div>
</div>
</template>
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)
};
}
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
};
}
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>
When we call this function
let { user, loading, error } = useAuth();
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
<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}} {{user.email}}</h2>
<h4>
<button @click="logout()">LOGOUT</button>
</h4>
<hr />
<br />
<ThingList />
</div>
(b3)<div v-else>
<LoginForm></LoginForm>
</div>
</div>
</template>
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>
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.
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
- code https://github.com/aaronksaunders/vue-composition-firebase-app-2
- blog https://dev.to/aaronksaunders/using-vue-composition-api-with-firebase-part-ii-25ki
Check Out The Video Series Starting Here:
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)
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 havesetup
function and there calluseAuth
. Every time you calluseAuth
you create a new listeneronAuthStateChanged
. 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
andprovide
for this.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 apiI mean in your
use/auth.js
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?
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.
please check out the recently uploaded video covering this here on @YouTube youtube.com/watch?v=3MLHCRep_MM