In this section, we will delve into the core concepts of Vuex, which is a state management pattern + library for Vue.js applications. Vuex serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.

Key Concepts

State

  • State is the single source of truth in Vuex. It is an object that holds the application-level state.
  • State can be accessed in any component by using the this.$store.state object.

Getters

  • Getters are similar to computed properties for stores. They allow you to compute derived state based on store state.
  • Getters can be accessed in any component by using the this.$store.getters object.

Mutations

  • Mutations are the only way to actually change state in a Vuex store. Each mutation has a string type and a handler function.
  • Mutations must be synchronous.

Actions

  • Actions are similar to mutations, but instead of mutating the state, actions commit mutations.
  • Actions can contain arbitrary asynchronous operations.

Practical Examples

State

// store.js
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    count: 0
  }
});

Getters

// store.js
export default new Vuex.Store({
  state: {
    count: 0
  },
  getters: {
    doubleCount: state => state.count * 2
  }
});

Mutations

// store.js
export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  }
});

Actions

// store.js
export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment');
      }, 1000);
    }
  }
});

Using Vuex in Components

Accessing State

<template>
  <div>{{ count }}</div>
</template>

<script>
export default {
  computed: {
    count() {
      return this.$store.state.count;
    }
  }
};
</script>

Using Getters

<template>
  <div>{{ doubleCount }}</div>
</template>

<script>
export default {
  computed: {
    doubleCount() {
      return this.$store.getters.doubleCount;
    }
  }
};
</script>

Committing Mutations

<template>
  <button @click="increment">Increment</button>
</template>

<script>
export default {
  methods: {
    increment() {
      this.$store.commit('increment');
    }
  }
};
</script>

Dispatching Actions

<template>
  <button @click="incrementAsync">Increment Async</button>
</template>

<script>
export default {
  methods: {
    incrementAsync() {
      this.$store.dispatch('incrementAsync');
    }
  }
};
</script>

Practical Exercises

Exercise 1: Create a Counter

  1. Objective: Create a simple counter application using Vuex.
  2. Steps:
    • Set up a Vuex store with a count state.
    • Add a mutation to increment the count.
    • Add an action to increment the count asynchronously.
    • Create a component to display the count and buttons to increment it synchronously and asynchronously.

Solution:

// store.js
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment');
      }, 1000);
    }
  }
});
// Counter.vue
<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
    <button @click="incrementAsync">Increment Async</button>
  </div>
</template>

<script>
export default {
  computed: {
    count() {
      return this.$store.state.count;
    }
  },
  methods: {
    increment() {
      this.$store.commit('increment');
    },
    incrementAsync() {
      this.$store.dispatch('incrementAsync');
    }
  }
};
</script>

Exercise 2: Add a Getter

  1. Objective: Add a getter to compute the double of the count.
  2. Steps:
    • Add a getter to the Vuex store to compute doubleCount.
    • Display doubleCount in the component.

Solution:

// store.js
export default new Vuex.Store({
  state: {
    count: 0
  },
  getters: {
    doubleCount: state => state.count * 2
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment');
      }, 1000);
    }
  }
});
// Counter.vue
<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ doubleCount }}</p>
    <button @click="increment">Increment</button>
    <button @click="incrementAsync">Increment Async</button>
  </div>
</template>

<script>
export default {
  computed: {
    count() {
      return this.$store.state.count;
    },
    doubleCount() {
      return this.$store.getters.doubleCount;
    }
  },
  methods: {
    increment() {
      this.$store.commit('increment');
    },
    incrementAsync() {
      this.$store.dispatch('incrementAsync');
    }
  }
};
</script>

Common Mistakes and Tips

  • Mutations must be synchronous: Avoid performing asynchronous operations inside mutations. Use actions for that purpose.
  • Naming conventions: Use consistent naming conventions for mutations and actions to make your code more readable and maintainable.
  • State normalization: Keep your state normalized to avoid deeply nested structures, which can be hard to manage.

Conclusion

In this section, we covered the fundamental concepts of Vuex: state, getters, mutations, and actions. We learned how to set up a Vuex store, access state and getters in components, and commit mutations and dispatch actions. These concepts are crucial for managing state in larger Vue.js applications. In the next module, we will explore more advanced Vuex patterns and how to use Vuex modules to organize your store.

© Copyright 2024. All rights reserved