Please checkout and subscribe to my video content on YouTube. Feel free to leave comments and suggestions for what content you would like to see.
YouTube Channel
Overview
For this first example, I will show how to integrate state management with Vuex into this application. I will not be integrating the store directly into the vue-composition functions, I will be accessing the store externally from the component that calls the vue-composition functions.
When the remote Firebase Database has been updated successfully, we will then dispatch calls to the store to update the data locally.
Set Up
Install vuex
npm install --save vuex
Create a new file called store.js
in the project root and add the following code which will make up the store that we are using for the project. Since vuex is not the primary objective of the blog post, I will not be covering vuex in detail
See more information on vuex here https://vuex.vuejs.org/
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
things: [],
},
mutations: {
addThing(state, payload) {
state.things = [payload, ...state.things];
},
deleteThing(state, payload) {
let newArray = state.things.filter(i => i.id !== payload);
state.things = newArray;
},
loadThings: (state, payload) => {
state.things = [...payload];
}
},
actions: {
loadThings: ({ commit }, payload) => {
commit("loadThings", payload);
},
addThing: ({ commit }, payload) => {
commit("addThing", payload);
},
deleteThing: ({ commit }, payload) => {
commit("deleteThing", payload);
}
}
});
export default store
import and set the store on the main vue instance, this will allow us access in our components
import Vue from "vue";
import App from "./App.vue";
import VueCompositionApi from "@vue/composition-api";
// New information from store
import store from "./store"
Vue.config.productionTip = false;
Vue.use(VueCompositionApi);
new Vue({
store, // <== ADD STORE HERE
render: h => h(App)
}).$mount("#app");
How To Update The Vuex Store
Below is the code from the ThingList.vue
component, I have only included the key sections of the code here in the blog.
The simplest integration of vuex it to simply access the store outside of the vue-composition function and ensure all of the vue-composition functions return a promise. On successful completion of the promise we will dispatch the appropriate action on the store in order to update the state.
methods: {
addThing(_name) {
this.createDocument({ name: _name }).then(_result => {
this.$store.dispatch("addThing", _result);
});
},
deleteThing(_id) {
this.deleteDocument(_id).then(_result => {
this.$store.dispatch("deleteThing", _result.id);
});
}
},
mounted() {
this.getCollection(/*{ limit: 5 }*/).then(_results => {
this.$store.dispatch("loadThings", _results.data);
});
}
How To Update App To Use Store in Template
Since we are now rendering reactive content from the store directly and not the vue-composition function use-collection we need to update the template to reflect that change.
we are getting the collection data from the store with $store.state.things
as the edit made to the v-for
in the template
<div v-for="item in $store.state.things" :key="item.id">
<div class="item-wrapper">
<div @click="getDocument(item.id)">
<div>{{item.name}}</div>
{{item.createdOn.toDate()}}
</div>
<button @click="deleteThing(item.id)">DELETE</button>
</div>
</div>
Changes To The Vue-Composition Functions
All of the changes will be ensuring a promise is returned from the functions and the results from a successful promise and unsuccessful promise are returned appropriately before updating the vuex store.
use-collections
(1) we now return the promise from the query
(2) when the promise is resolved, we return an object with a property data containing the query results.
(3) when the promise is rejected, we return an object with a property error containing the error
const getCollection = ({ query, orderBy, limit } = queryOptions) => {
state.loading = true;
state.error = null;
let resultArray = [];
let theQuery = query
? db.collection(collectionName).where(_query)
: db.collection(collectionName);
theQuery = limit ? theQuery.limit(limit) : theQuery;
theQuery = orderBy ? theQuery.orderBy(orderBy) : theQuery;
// (1) we now return the promise from the query
return theQuery
.get()
.then(querySnapshot => {
querySnapshot.forEach((doc)=> {
resultArray.push({ id: doc.id, ...doc.data() });
});
state.collectionData = resultArray;
state.error = null;
// (2) when the promise is resolved, we return an object
// with a property data containing the query results
return { data : resultArray }
})
.catch((error) => {
console.log("Error getCollection: ", error);
state.error = error;
// (3) when the promise is rejected, we return an object
// with a property error containing the error
return { error };
})
.finally(() => {
state.loading = false;
});
};
use-document
deleteDocument
(1) we now return the promise from the query
(2) when the promise is resolved, we return an object with a property id containing the id of the deleted document.
(3) when the promise is rejected, we return an object with a property error containing the error
const deleteDocument = _documentId => {
state.loading = true;
state.error = null;
// (1) we now return the promise from the query
return db
.collection(collectionName)
.doc(_documentId)
.delete()
.then(() => {
console.log("Document successfully deleted!");
state.error = null;
state.documentData = null;
// (2) when the promise is resolved, we return an object
// with a property id containing the id of the deleted document
return { id: _documentId };
})
.catch(error => {
console.error("Error removing document: ", error);
state.error = error;
state.documentData = null;
// (3) when the promise is rejected, we return an object
// with a property error containing the error
return { error };
})
.finally(() => {
state.loading = false;
});
};
createDocument
(1) we now return the promise from the query
(2) when the promise is resolved, get the id of the new document which is needed to get the whole document back from firebase
(3) Now that we have the document and all of the data, return the document.
(4) when the promise is rejected, we return an object with a property error containing the error
const createDocument = _documentData => {
state.loading = true;
state.error = null;
// (1) we now return the promise from the query
return db
.collection(collectionName)
.add({
..._documentData,
createdOn: firebase.firestore.FieldValue.serverTimestamp()
})
.then(docRef => {
// (2) get the id of the new document which is needed to
// get the whole document back from firebase
state.error = null;
state.documentData.id = docRef.id;
return docRef.get();
})
.then(_doc => {
// (3) Now that we have the document and all of the data, return
// the document
return { id: _doc.id, ..._doc.data() };
})
.catch(function(error) {
// The document probably doesn't exist.
console.error("Error createDocument: ", error);
state.error = error;
state.documentData = null;
// (4) when the promise is rejected, we return an object with a
// property error containing the error
return { error };
})
.finally(() => {
state.loading = false;
});
};
Thanks for taking a look at this enhancement to the vue-composition functions integration into a project with firebase and vuex. Please leave a comment or suggestion here or in the github repo.
Source Code
- Please Note: The source code in on a branch from the main repo, please me sure you are on the branch "vuex-1" when reviewing the code
About Clearly Innovative
Clearly Innovative is a 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!