Use Vuexfire to simplify your application state management
Tags: javascript, beginners, vue, webdev
Published: 2020-11-18 13:09:27

The problem tackled by VuexFire is pretty simple: **keeping you state data in sync with the data in Firebase Firestore or RTDB**. If you're familiar with Vue and Vuex, I'm sure you've faced this problem before. Let's think on a simple app like a to-do list.

- Every time a user creates a new to-do, a Vuex action sends a request to the backend and commits a mutation to update the application state.
- When the user completes a to-do, a Vuex action sends a request to the backend and then commits another mutation to update that particular to-do in the state.
- When a user deletes a to-do, a Vuex action sends another request to the backend and then commits another mutation to delete that particular to-do from the state.

As you can see, **every user interaction requires a Vuex action and a mutation** to keep the application in sync with the data in the backend. **VuexFire simplies this for us a lot, by binding our application state to Firestore or RTDB and updating the state automatically without the need to write or commit any mutation.** It archieves this by providing its own set of mutations that take care of common things like adding or deleting items from an array or updating an object.

Let me walk you through a basic example.

> I'm assuming we already have a Vue CLI project scaffolded with Vuex installed. You can find more info on how to do that [here](https://cli.vuejs.org/guide/prototyping.html).

### Retrieving data with Vuexfire

Let's say we want to build an app to manage books. The first thing I'd need to do is to install the Firebase and Vuexfire dependencies in the Vue project running `npm i firebase vuexfire`.

Then I created a _firebase.js_ file in which I'll configure the Firebase SDK with the project keys and, assuming that our books will be stored in a Firebase collection named _books_, create a reference to it. It'll look like this:

```js
// File src/firebase.js

import firebase from 'firebase/app'
import 'firebase/firestore'

const firebaseApp = firebase.initializeApp({
  apiKey: process.env.VUE_APP_APIKEY,
  authDomain: process.env.VUE_APP_AUTHDOMAIN,
  databaseURL: process.env.VUE_APP_DATABASEURL,
  projectId: process.env.VUE_APP_PROJECTID,
  storageBucket: process.env.VUE_APP_STORAGEBUCKET,
  messagingSenderId: process.env.VUE_APP_MESSAGINGSENDERID,
  appId: process.env.VUE_APP_APPID,
})

const db = firebaseApp.firestore()
// collection reference
const booksCollection = db.collection('books')

// exports collection reference to use it in the Vuex Store
export { booksCollection }
```

> **Note:** In this example I'm loading the Firebase keys from environment variables but you can load the values directly 😉

In the application store, I'd want to keep all the books in a property named _allBooks_ inside the state, so I''ll just have to create it and initialise it to an empty array:

```js
// File src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    // list of all books initialized empty
    allBooks: [],
  },
  actions: {},
  mutations: {},
  getters: {}
}
```

The next thing I'd need to do is to create a Vuex action that binds the _allBooks_ property from the state with a Firestore query that returns the data from the _books_ collection:

```js
// File src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

// imports collection reference from firebase.js file
import { booksCollection } from '@/firebase'
// imports required to bind the state to Firebase using Vuexfire
import { firestoreAction, vuexfireMutations } from 'vuexfire'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    allBooks: [],
  },
  actions: {
    /**
     * @param context deconstructed to get only the method to create the ref
     */
    bindBooks: firestoreAction(({ bindFirestoreRef }) => {
      // return the promise returned by `bindFirestoreRef`
      return bindFirestoreRef(
        'allBooks',
        booksCollection.orderBy('author', 'asc')
      )
    }),
  },
  mutations: {
    // adds Vuexfire built-in mutations
    ...vuexfireMutations,
  },
  getters: {
    allBooks: (state) => {
      return state.allBooks
    },
  },
})
```

As you can see, the action _bindBooks_ is a firestoreAction, which I've imported from vuexfire. This action returns a promise resolved by _bindFirestoreRef()_, which receives two parameters: the attribute in our state where we want to bind the data (in our case _allBooks_), and the query that will return the data (in our case, all books ordered by the author).

I also imported _vuexfireMutations_ and added them inside our mutations using the destructuring operator _..._. Finally, I created a simple getter that returns the _allBooks_ list from the state, which I'll use in a component.

The next step is to create a component to display all the books. A very simple one would be like this:

```js
// App.vue file

<template>
  <div id="app">
    <h1>My Book List app</h1>
    <div class="book" v-for="book in allBooks" :key="book.id">
      <h2>{{ book.title }}</h2>
      <p>Written by {{ book.author }}</p>
      <p>{{ book.summary }}</p>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  name: 'App',
  mounted() {
    this.$store.dispatch('bindBooks')
  },
  computed: {
    ...mapGetters(['allBooks']),
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.book {
  margin: 2rem;
  border-bottom: 1px solid gray;
}
</style>
```

As you can see, on the _mounted_ lifecycle hook, I dispatch the action I created in the store, which will get the list of data from the Firestore _books_ collection and then all books will be displayed. That awesome because it didn't took too much code to build this, but **the best part is that if we add, delete or update books directly from the Firebase console, our app will refresh its store automatically**.

![Adding and removing books](https://antonioufano.com/image_uploads/1605259669684.gif)

### Writting to Firestore with Vuexfire

Up until now, I've shown you how to use Vuexfire read and automatically keep the application state on sync with Firestore but in a real world app, we'd need to create, update or delete documents from our database. For that scenario, Vuexfire does not provide any methods or helpers and, [as mentioned in the documentation](https://vuefire.vuejs.org/vuexfire/writing-data.html), it recommends you to use the Firebase JS SDK.

Two simple Vuex actions to add and delete books in the collection would look like this:

```js
// File: src/store/index.js

// .....
export default new Vuex.Store({
  state: {
    allBooks: [],
  },
  actions: {
    /**
     * @param context deconstructed to get only the method to create the ref
     */
    bindBooks: firestoreAction(({ bindFirestoreRef }) => {
      // return the promise returned by `bindFirestoreRef`
      return bindFirestoreRef(
        'allBooks',
        booksCollection.orderBy('author', 'asc')
      )
    }),
    addBook: firestoreAction((context, payload) => {
      console.log('Adding a new book!')
      return booksCollection.add(payload.newBook)
    }),
    deleteBook: firestoreAction((context, payload) => {
      console.log(`Deleting book ${payload.bookId}`)
      return booksCollection.doc(payload.bookId).delete()
    }),
  },
})
```

> Note: we'd need to pass the payload for each action from the component. You can see the detailed code in [this file of the repo](https://github.com/uF4No/vuexfire-example/blob/main/src/App.vue)

Notice that in these actions, **we're not commiting any mutations as we usually do in Vuex**.That's because the first action _bindBooks_ will take care of keeping the application state in sync with Firestore. So even though we have to actually trigger the _add()_ or _delete()_ functions, **with Vuexfire we don't have to worry about refreshing our state**.

You can find all the code of this article in [the following repo in GitHub](https://github.com/uF4No/vuexfire-example).

## Conclusion

As you can see, Vuexfire is a pretty nice tool you can use to simplify the state management of your Vue app. I've integrated it in [one of my latest projects](https://quicktalks.io) and it has helped me to get rid of a ton of code and, the less code you have, the less bugs you'll probably find 😅

Of course, it has its limitations, like for example, pagination. However thanks to some guidance I found on StackOverflow, I've been able to code a simple pagination that works for me. I'll share it with you in the next article.

If you liked this article, you can [follow me on Twitter](https://twitter.com/uf4no) where I share dev tips an interesting articles and updates about the progress of my projects 🤙

Happy coding!

* * *

_This article was originally posted in [my blog](https://antonioufano.com/blog) where you can find other articles about web development focused on Node.js, Vue, Laravel and anything related with product development._