Vuex
01 / 02

State, Getters, Mutations & Actions

State, Getters, Mutations & Actions

Store Structure

import { createStore } from 'vuex';

const store = createStore({
  state() {
    return { todos: [], loading: false };
  },

  getters: {
    // Cached like a computed property — recomputes only when todos changes
    completedTodos: (state) => state.todos.filter((t) => t.completed),
  },

  mutations: {
    // MUST be synchronous — devtools relies on a clean before/after
    // snapshot per mutation for time-travel debugging
    ADD_TODO(state, todo) {
      state.todos.push(todo);
    },
    SET_LOADING(state, value) {
      state.loading = value;
    },
  },

  actions: {
    // Async work lives here; the actual state change is still a
    // synchronous mutation — actions never mutate state directly
    async fetchTodos({ commit }) {
      commit('SET_LOADING', true);
      const todos = await api.getTodos();
      todos.forEach((todo) => commit('ADD_TODO', todo));
      commit('SET_LOADING', false);
    },
  },
});

Using the Store in Components

export default {
  computed: {
    // Reading state — direct access or the mapState helper
    todos() { return this.$store.state.todos; },
  },
  methods: {
    addTodo(text) {
      // commit -> mutations (sync), dispatch -> actions (can be async)
      this.$store.commit('ADD_TODO', { text, completed: false });
    },
    loadTodos() {
      this.$store.dispatch('fetchTodos');
    },
  },
};

// Helper mapping shorthand
import { mapState, mapActions } from 'vuex';

export default {
  computed: { ...mapState(['todos', 'loading']) },
  methods: { ...mapActions(['fetchTodos']) },
};

// NEVER mutate state directly from a component — bypasses tracking
// entirely; store.state.todos.push(x) breaks devtools history and
// throws in strict mode.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free