Vue 3 Composition API: Practical Examples and Patterns

Now that Vue 3 is released, let’s dive deeper into the Composition API with practical examples you can use in real projects. I’ve been using the Composition API in production for several months via the Vue 2 plugin, and these patterns have proven invaluable.

Understanding Reactivity Fundamentals

The Composition API introduces two main reactive primitives: ref and reactive. Understanding when to use each is crucial.

import { ref, reactive, toRefs } from 'vue'

// ref - for primitives and replacing entire values
const count = ref(0)
const user = ref(null)

count.value++  // Access via .value
user.value = { name: 'John' }  // Can replace entirely

// reactive - for objects you'll mutate
const state = reactive({
  users: [],
  currentPage: 1,
  filters: { search: '', status: 'active' }
})

state.users.push(newUser)  // Direct mutation, no .value
state.filters.search = 'John'

// toRefs - destructure reactive objects while keeping reactivity
const { users, currentPage } = toRefs(state)
// users.value is reactive

Building Reusable Composables

Composables are the heart of the Composition API. Here’s a real-world example for handling API calls with loading states, error handling, and caching:

// composables/useApi.js
import { ref, shallowRef } from 'vue'

export function useApi(fetcher) {
  const data = shallowRef(null)
  const error = ref(null)
  const isLoading = ref(false)
  
  async function execute(...args) {
    isLoading.value = true
    error.value = null
    
    try {
      data.value = await fetcher(...args)
      return data.value
    } catch (e) {
      error.value = e
      throw e
    } finally {
      isLoading.value = false
    }
  }
  
  return {
    data,
    error,
    isLoading,
    execute
  }
}

// Usage in component
import { useApi } from '@/composables/useApi'
import { userService } from '@/services/user'

export default {
  setup() {
    const { data: users, isLoading, error, execute: loadUsers } = 
      useApi(userService.getAll)
    
    onMounted(loadUsers)
    
    return { users, isLoading, error, loadUsers }
  }
}

Composable for Local Storage

Here’s another practical composable for syncing state with localStorage:

// composables/useLocalStorage.js
import { ref, watch } from 'vue'

export function useLocalStorage(key, defaultValue) {
  const stored = localStorage.getItem(key)
  const value = ref(stored ? JSON.parse(stored) : defaultValue)
  
  watch(value, (newValue) => {
    localStorage.setItem(key, JSON.stringify(newValue))
  }, { deep: true })
  
  return value
}

// Usage - automatically persisted!
const theme = useLocalStorage('theme', 'light')
const recentSearches = useLocalStorage('searches', [])

Lifecycle Hooks in Composition API

Lifecycle hooks are imported and called directly in setup():

import { onMounted, onUnmounted, onUpdated, onBeforeUnmount } from 'vue'

export default {
  setup() {
    onMounted(() => {
      console.log('Component mounted')
      window.addEventListener('resize', handleResize)
    })
    
    onBeforeUnmount(() => {
      window.removeEventListener('resize', handleResize)
    })
    
    // Can call multiple times - they all run
    onMounted(() => {
      console.log('Second onMounted')
    })
  }
}

Key Takeaways

  • Use ref for primitives and replaceable values, reactive for objects you’ll mutate
  • Extract reusable logic into composable functions
  • Composables can be composed together for complex functionality
  • Lifecycle hooks are imported functions called within setup()

References


Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.