Image

scaling

6 min read
Last update: December 19, 2021

This course was created with Nuxt 2.3.4. Please check the Nuxt docs if you run into any issues using a newer version.

Using Vuex

In the previous two lessons, we reviewed how Nuxt provides an asyncData hook to make Axios calls server or client-side before a component is loaded. As you may already know, as your application grows it becomes important to use a Vuex store to manage state. In this lesson we’ll do just that, by first organizing our API calls into a Service, and then creating a Vuex module for our events.

It’s important that you’re already familiar with Vuex before taking this lesson. If you are not, I’d recommend going through our Mastering Vuex course here on Vue Mastery.

Creating an Event Service

This should be familiar if you took the API calls with Axios lesson in our Real World Vue Course. We need to create a new folder called /services in our root directory and create a new file that looks something like this:

📃 /services/EventService.js

import axios from 'axios'

const apiClient = axios.create({
baseURL: `http://localhost:3000`,
withCredentials: false,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})

export default {
getEvents() {
return apiClient.get('/events')
},
getEvent(id) {
return apiClient.get('/events/' + id)
}
}

Here we’re configuring Axios and creating methods which invoke our two API calls. These each return a promise. We then simply need to use these methods in our two page components.

📃 /pages/index.vue

...
<script>
    import EventCard from '@/components/EventCard.vue'
    import EventService from '@/services/EventService.js' // <----

    export default {
        ...
        async asyncData({
                error
            }) {
                try {
                    const {
                        data
                    } = await EventService.getEvents()
                        ...
                        ``
                    `

📃 **/pages/event/\_id.vue**

`
                    ``
                    html
                        ...
                        <
                        script >
                        import EventService from '@/services/EventService.js' // <----

                    export default {
                        ...
                        async asyncData({
                                error,
                                params
                            }) {
                                try {
                                    const {
                                        data
                                    } = await EventService.getEvent(params.id)
                                        ...
                                        ``
                                    `

In our browser everything should be just the same, except now our code is a little more modular. If you’re following along, do make sure your API server and development server is running.

## Setting up your VueX store with Nuxt

Several folders were created when you initially scaffolded your Nuxt app using `
                                    create - nuxt - app`. One of those folders was a `
                                    store` folder. This is where all your VueX-related code will live.

If Nuxt detects that this store folder exists it will automatically import Vuex into your project, and add the `
                                    store` option to the root Vue instance. By default every `.js` file inside the `
                                    store` directory is transformed to a namespaced module.

In a minute we’ll create an `
                                    event``
                                    s``.js` Vuex module inside of our `
                                    store` directory. This will create a namespaced Vuex module named `
                                    even``
                                    ts` for us. It’ll be this module that calls our EventService to do API calls and set our events data into it’s state. Visually it looks something like this:

![](https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2F1578373588807_0.jpg?alt=media&token=880d1cb1-75a1-4db8-8fe5-4df6a3cb58d7)

In code it looks like this:

📃 **/store/events.js**

`
                                    ``
                                    javascript
                                    import EventService from '@/services/EventService.js'
                                    export const state = () => ({
                                        events: []
                                    })
                                    export const mutations = {
                                        SET_EVENTS(state, events) {
                                            state.events = events
                                        }
                                    }
                                    export const actions = {
                                        fetchEvents({
                                            commit
                                        }) {
                                            return EventService.getEvents().then(response => {
                                                commit('SET_EVENTS', response.data)
                                            })
                                        }
                                    }
                                    ``
                                    `

Take note that `
                                    state` value should **always be a** `
                                    function ` to avoid unwanted _shared_ state on the server side. Another thing to keep in mind is that we need to return a promise from our `
                                    fetchEvent` action. This will help our component know when this promise is resolved so it can continue loading the page.

Next can use this Vuex module inside our `
                                    index.vue` component, but we’re going to need to learn another page component hook Nuxt gives us. The `
                                    fetch` page component hook works on the client & server-side to fill the store before rendering the page. Unlike `
                                    asyncData` this doesn’t have a return value that merges with component data, which we don’t need anymore.

📃 **/pages/index.vue**

`
                                    ``
                                    html
                                        <
                                        script >
                                        import EventCard from '@/components/EventCard.vue'
                                    import {
                                        mapState
                                    } from 'vuex' // <--- To map event
                                    export default {
                                        ...
                                        async fetch({
                                            store,
                                            error
                                        }) {
                                            try {
                                                await store.dispatch('events/fetchEvents')
                                            } catch (e) {
                                                ...
                                            },
                                            computed: mapState({
                                                events: state => state.events.events
                                            })
                                        }
</script>

That’s all there is to it, and now we can list all events by using Vuex.

Using Vuex for the EventShow Page

Now let’s use Vuex for our other page component which loads up a single event. We’ll start by adding some code to our events Vuex module.

📃 /store/events.js

import EventService from '@/services/EventService.js'
export const state = () => ({
events: [],
event: {}
})
export const mutations = {
...
SET_EVENT(state, event) {
state.event = event
}
}
export const actions = {
...
fetchEvent({ commit }, id) {
return EventService.getEvent(id).then(function(response) {
commit('SET_EVENT', response.data)
})
}
}

Then we’ll just need to update our id component, and while we’re in there, let’s go ahead and add some template and style code.

📃 /pages/event/_id.vue

<template>
    <div>
        <div class="event-header">
            <span class="eyebrow">
                @{{ event.time }} on {{ event.date }}
            </span>
            <h1 class="title">
                {{ event.title }}
            </h1>
            <h5>Organized by {{ event.organizer ? event.organizer.name : '' }}</h5>
            <h5>Category: {{ event.category }}</h5>
        </div>

        <span name="map">
            <h2>Location</h2>
        </span>

        <address>{{ event.location }}</address>

        <h2>Event details</h2>
        <p>{{ event.description }}</p>

        <h2>
            Attendees
            <span class="badge -fill-gradient">
                {{ event.attendees ? event.attendees.length : 0 }}
            </span>
        </h2>
        <ul class="list-group">
            <li v-for="(attendee, index) in event.attendees" :key="index" class="list-item">
                <b>{{ attendee.name }}</b>
            </li>
        </ul>
    </div>
</template>
<script>
    import {
        mapState
    } from 'vuex'
    export default {
        ...
        async fetch({
            store,
            params,
            error
        }) {
            try {
                await store.dispatch('events/fetchEvent', params.id)
            } catch (e) {
                error({
                    statusCode: 503,
                    message: 'Unable to fetch event #' + params.id
                })
            }
        },
        computed: mapState({
            event: state => state.events.event
        })
    }
</script>

<style scoped>
    .prompt-box {
        position: relative;
        overflow: hidden;
        padding: 1em;
        margin-bottom: 24px;
        transform: scaleY(1);
    }

    .prompt-box>.title {
        margin: 0 0 0.5em;
    }

    .prompt-box>.title>.meta {
        margin-left: 10px;
    }

    .prompt-box>.actions {
        display: flex;
        align-items: center;
    }

    .prompt-box>button {
        margin-right: 0.5em;
    }

    .prompt-box>button:last-of-type {
        margin-right: 0;
    }

    .location {
        margin-bottom: 0;
    }

    .location>.icon {
        margin-left: 10px;
    }

    .event-header>.title {
        margin: 0;
    }

    .list-group {
        margin: 0;
        padding: 0;
        list-style: none;
    }

    .list-group>.list-item {
        padding: 1em 0;
        border-bottom: solid 1px #e5e5e5;
    }
</style>

Now when we go into our browser we can see everything work as expected, now using Vuex.

To ReVue

In this lesson we refactored our Axios API calls into an EventService, created an Events Vuex Module to call that service, and used Nuxt’s fetch hook to dispatch the action which populates our state. In the next lessons we’ll dive into nuxt.config, and then discover two different ways to deploy our Nuxt app.