Vue.js Vuex Getters
Introduction
When managing state in Vue.js applications, you'll often need to derive new data from your store's state. Rather than computing this derived state in multiple components (which could lead to code duplication), Vuex provides getters - a powerful feature that allows you to define computed properties for your store.
Think of Vuex getters as the store's equivalent of Vue component's computed properties. They help you extract, filter, and transform data from your store's state before presenting it to your components.
In this tutorial, we'll explore:
- What Vuex getters are and why they're useful
- How to define and access getters
- Creating getters that accept arguments
- Real-world examples of getters in action
Understanding Vuex Getters
What are Getters?
Getters are functions in your Vuex store that take the state as their first argument and return computed values based on that state. They're particularly useful when multiple components need to use the same computed property.
Why Use Getters?
- Avoid Duplication: Define data transformation logic once in your store
- Caching: Results are cached based on dependencies and only re-evaluated when dependencies change
- Centralization: Keep data transformation logic close to your state
- Reusability: Use the same derived state across multiple components
Defining and Using Basic Getters
Defining Getters
Let's create a simple store with some state and define getters to access derived information:
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
todos: [
{ id: 1, text: 'Learn Vue', completed: true },
{ id: 2, text: 'Learn Vuex', completed: false },
{ id: 3, text: 'Build something awesome', completed: false }
]
}
},
getters: {
// Getter to count completed todos
completedTodos(state) {
return state.todos.filter(todo => todo.completed)
},
// Getter to count incomplete todos
incompleteTodos(state) {
return state.todos.filter(todo => !todo.completed)
},
// Getter that returns a count
todoCount(state) {
return state.todos.length
}
}
})
export default store
Accessing Getters in Components
You can access getters in your components in several ways:
Option 1: Using this.$store.getters
<template>
<div>
<p>Total todos: {{ $store.getters.todoCount }}</p>
<p>Completed todos: {{ $store.getters.completedTodos.length }}</p>
<p>Todos left: {{ $store.getters.incompleteTodos.length }}</p>
</div>
</template>
Option 2: Using mapGetters
Helper
The mapGetters
helper maps store getters to local computed properties:
<template>
<div>
<p>Total todos: {{ todoCount }}</p>
<p>Completed todos: {{ completedTodos.length }}</p>
<p>Todos left: {{ incompleteTodos.length }}</p>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
// Map store getters to local computed properties
...mapGetters([
'todoCount',
'completedTodos',
'incompleteTodos'
]),
// You can still define other computed properties
progress() {
if (this.todoCount === 0) return 0
return Math.round((this.completedTodos.length / this.todoCount) * 100)
}
}
}
</script>
Option 3: Using mapGetters
with Custom Names
You can also rename getters when mapping them:
computed: {
...mapGetters({
count: 'todoCount',
finished: 'completedTodos',
remaining: 'incompleteTodos'
})
}
Advanced Getters
Getters Accessing Other Getters
Getters can access other getters via the second argument:
getters: {
todoCount(state) {
return state.todos.length
},
completedTodoCount(state) {
return state.todos.filter(todo => todo.completed).length
},
// This getter uses other getters
progressPercentage(state, getters) {
if (getters.todoCount === 0) return 0
return Math.round((getters.completedTodoCount / getters.todoCount) * 100)
}
}
Getters with Arguments (Method-Style Getters)
Sometimes you need to pass arguments to your getters, like filtering an array based on a parameter. You can return a function from your getter to achieve this:
getters: {
// Getter that returns a function
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
},
// Filter todos by completion status
filterTodosByStatus: (state) => (status) => {
return state.todos.filter(todo => todo.completed === status)
}
}
Using method-style getters in components:
<template>
<div>
<p>Todo #2: {{ $store.getters.getTodoById(2).text }}</p>
<h3>All completed todos:</h3>
<ul>
<li v-for="todo in $store.getters.filterTodosByStatus(true)" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</div>
</template>
Real-World Example: E-commerce Shopping Cart
Let's explore a more practical example of using Vuex getters in an e-commerce application:
const store = createStore({
state() {
return {
cart: [
{ id: 1, name: 'Wireless Headphones', price: 99.99, quantity: 1 },
{ id: 2, name: 'Smartphone', price: 699.99, quantity: 1 },
{ id: 3, name: 'Laptop Charger', price: 29.99, quantity: 2 }
],
taxRate: 0.08,
shippingFee: 5.99
}
},
getters: {
// Get all items in cart
cartItems(state) {
return state.cart
},
// Count total number of items
cartItemCount(state) {
return state.cart.reduce((total, item) => total + item.quantity, 0)
},
// Calculate subtotal (before tax and shipping)
cartSubtotal(state) {
return state.cart.reduce((total, item) => {
return total + (item.price * item.quantity)
}, 0)
},
// Calculate tax amount
taxAmount(state, getters) {
return getters.cartSubtotal * state.taxRate
},
// Calculate final total
cartTotal(state, getters) {
return getters.cartSubtotal + getters.taxAmount + state.shippingFee
},
// Find specific item in cart
getCartItemById: (state) => (id) => {
return state.cart.find(item => item.id === id)
}
}
})
Using these getters in a shopping cart component:
<template>
<div class="shopping-cart">
<h2>Shopping Cart ({{ cartItemCount }} items)</h2>
<div v-for="item in cartItems" :key="item.id" class="cart-item">
<span>{{ item.name }}</span>
<span>{{ item.quantity }} x ${{ item.price.toFixed(2) }}</span>
<span>${{ (item.price * item.quantity).toFixed(2) }}</span>
</div>
<div class="cart-summary">
<div>Subtotal: ${{ cartSubtotal.toFixed(2) }}</div>
<div>Tax ({{ taxRate * 100 }}%): ${{ taxAmount.toFixed(2) }}</div>
<div>Shipping: ${{ shippingFee.toFixed(2) }}</div>
<div class="total">Total: ${{ cartTotal.toFixed(2) }}</div>
</div>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
export default {
computed: {
// Map state properties
...mapState(['taxRate', 'shippingFee']),
// Map getters
...mapGetters([
'cartItems',
'cartItemCount',
'cartSubtotal',
'taxAmount',
'cartTotal'
])
}
}
</script>
Best Practices for Vuex Getters
-
Use Getters for Derived State: If you need to transform or filter state data, use getters rather than computing it in your components.
-
Avoid Side Effects: Getters should be pure functions that only compute and return values based on state, without modifying state or performing side effects.
-
Keep Getters Simple: Each getter should do one thing and do it well. Combine multiple getters if you need more complex logic.
-
Use Method-Style Getters Sparingly: While convenient, method-style getters lose the caching benefit of regular getters. Use them only when parameter-based filtering is needed.
-
Consistent Naming: Use a consistent naming convention for your getters to make your code more readable.
Summary
Vuex getters are powerful tools for accessing and computing derived state in your Vue.js applications. They help you:
- Keep your components cleaner by moving data transformation logic into your store
- Avoid duplicating computation logic across components
- Benefit from automatic caching for better performance
- Pass parameters to getters when needed
By using getters effectively in your Vuex store, you can create more maintainable and efficient Vue.js applications with centralized state management.
Additional Resources and Exercises
Resources
Exercises
-
Basic Getters: Create a todo application with getters to filter todos by their completion status and priority.
-
Method-Style Getters: Extend the todo app to include a getter that filters todos by both status and a search query parameter.
-
Complex Getters: Build a small e-commerce application with getters for filtering products by category, calculating discounts, and summarizing the shopping cart.
-
Performance Optimization: Profile your application's performance with and without getters to understand the caching benefits.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)