Schwesi Design Blog Post

Understanding state in modern javascript frameworks might prove challenging. One of the main reasons seems to be the many ways of managing state. In React for example one has to choose between Hooks, Context API and Apollo State among others more outlandish options. For vue and therefore nuxtjs many state management solutions exist, however there is one recommended way, which makes the developers choice much less frustrating.

At the time of this writing Nuxtjs 3 has not yet been released, therefore this post focuses on Nuxtjs 2.

Nuxtjs uses a vuex store to manage the state of the application. Everything containing your store will reside in the ‘store’ folder. Once again not leaving anything to guessing, unifying the folder structure, as well as the code base, making the application’s code easier maintainable and less error prone. Once the folder has been created all you need is an index.js to start managing the application’s state. Just by creating this file in this folder nuxtjs will automatically import vuex, and add the store option to the root vue instance. Doesn’t get much easier than that, does it?

This is all nice and fun, however in a real world application most likely the store is complex and will therefore be separated into different files. In the past, using classic mode, a typical setup for authentication logic might have looked like this:

				
				// store/index.js
				import Vue from 'vue'
				import Vuex from 'vuex'
				import auth from './modules/auth'
				import auth from './modules/base'

				Vue.use(Vuex)

				export default () => {
					return new Vuex.Store({
						actions: {
							nuxtServerInit ({ commit , { req ) {
								if (req.session.user && req.session.token) {
									commit('auth/SET_USER', req.session.user)
									commit('auth/SET_TOKEN', req.session.token
								}
							}
						},
						modules: {
							auth,
							base
						}
					})
				}
				store/modules/auth.js
				const state = () => ({
					user: null,
					token: null
				})

				const getters = {
					getToken (state) {
						return state.token
					},
					getUser (state) {
						return state.user
					}
				}

				const mutations = {
					SET_USER (state, user) {
						state.user = user
					},
					SET_TOKEN (state, token) {
						state.token = token
					}
				}

				const actions = {
					async register ({ commit }, { name, slug, email, password ) {
						try {
							const { data } = await this.$axios.('/users', { name, slug, email, password })
							commit('SET_USER', data)
						} catch (err) {
							commit('base/SET_ERROR', err.response.data.message, { root: true })
							throw err
						}
					},
				}

				export default {
					namespaced: true,
					state,
					getters,
					mutations,
					actions
				}
				
			

Please notice the lines commit('base/SET_ERROR', err.response.data.message, { root: true }), which calls the mutation in another module (called base). And the namespaced: true option, which was required for this to work.

Nowadays, the new 'modules mode' makes this much easier to implement. Every .js file inside the store directory automatically becomes a namespaced module. This means, you can have all the files in one folder and 'namespaced = true' is not required anymore. One other thing to note, is that the state value should always be a function to avoid unwanted shared state on the server side.

Here is how the above code would look in modules mode:

				
				// store/index.js
				export const state = () => ({})

				export const actions = {
					async nuxtServerInit ({ commit }, { req }) {
						if (req.session.user && req.session.token) {
							commit('auth/SET_USER', req.session.user)
							commit('auth/SET_TOKEN', req.session.token)
						}
					}
				}

				// store/auth.js
				const state = () => ({
					user: null,
					token: null
				})

				const getters = {
					getUser (state) {
						return state.user
					},
					getToken (state) {
						return state.token
					}
				}

				const mutations = {
					SET_USER (state, user) {
						state.user = user
					},
					SET_TOKEN (state, token) {
						state.token = token
					}
				}

				const actions = {
					async register ({ commit }, { name, slug, email, password }) {
						try {
							const { data } = await this.$axios.post('/users', { name, slug, email, password })
							commit('SET_USER', data)
						} catch (err) {
							commit('base/SET_ERROR', err.response.data.message, { root: true })
							throw err
						}
					}
				}

				export default {
					state,
					getters,
					mutations,
					actions
				}
				
			

This is quite straight forward and nuxt does the hard lifting for us. There is however, one more thing.

The nuxtServerInit Action

This special action can be defined in ‘store/index.js’ and will be called with the context from the server side, given that the mode of the application is universal. This might sound confusing at first, it basically means that this action will be called on server-side before rendering requested routes. So sticking with the authentication example the nuxtServerInit action might be used to access the authenticated user through sessions and set the state accordingly:

				
				export const actions = {
					nuxtServerInit ({ commit }, { req }) {
						if (req.session.user && req.session.token) {
							commit('auth/SET_USER', req.session.user)
							commit('auth/SET_TOKEN', req.session.token)
						}
					}
				}
				
			

So there you have it! State management in nuxtjs is not rocket science and can be done by mere humans.

Sources

Next Post

VPS Setup VPS Setup
scroll to top