0
0
Vueframework~15 mins

Composable with reactive state in Vue - Deep Dive

Choose your learning style9 modes available
Overview - Composable with reactive state
What is it?
A composable with reactive state in Vue is a reusable function that manages and shares reactive data and logic across components. It uses Vue's reactive system to keep data in sync automatically when it changes. This helps organize code better by separating concerns and avoiding repetition. Composables make it easy to build flexible and maintainable Vue applications.
Why it matters
Without composables managing reactive state, Vue components would have duplicated logic and scattered state management, making apps harder to maintain and scale. Composables solve this by letting developers write stateful logic once and reuse it everywhere. This leads to cleaner code, faster development, and fewer bugs, improving the user experience and developer happiness.
Where it fits
Before learning composables with reactive state, you should understand Vue's basic reactivity system, including ref and reactive, and how Vue components work. After mastering composables, you can explore advanced state management solutions like Pinia or Vuex, and learn about server-side rendering and Vue's Composition API in depth.
Mental Model
Core Idea
A composable with reactive state is like a smart toolbox that holds live data and logic, which updates automatically and can be shared across different parts of your app.
Think of it like...
Imagine a shared whiteboard in a classroom where everyone can write and erase notes. When one student updates the notes, everyone else instantly sees the change without needing to ask. The composable is the whiteboard, and the reactive state is the live notes.
┌─────────────────────────────┐
│       Composable Function    │
│ ┌─────────────────────────┐ │
│ │ Reactive State (ref/reactive) │
│ └─────────────────────────┘ │
│                             │
│ ┌─────────────────────────┐ │
│ │ Logic & Methods          │
│ └─────────────────────────┘ │
└─────────────┬───────────────┘
              │
              ▼
┌─────────────────────────────┐
│ Vue Components Using Composable │
│ - Access reactive state          │
│ - Call logic methods            │
└─────────────────────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Vue Reactivity Basics
🤔
Concept: Learn how Vue tracks changes in data using reactive and ref.
Vue provides reactive() to make an object reactive and ref() to create a reactive primitive value. When these reactive values change, Vue automatically updates the parts of the UI that depend on them. For example, ref(0) creates a reactive number that updates the UI when changed.
Result
You can create reactive data that Vue tracks and updates in the UI automatically.
Understanding Vue's reactivity is essential because composables rely on this system to keep shared state live and synchronized.
2
FoundationWhat Is a Composable Function?
🤔
Concept: A composable is a plain function that uses Vue's reactive APIs to encapsulate state and logic.
A composable function returns reactive state and methods. It can be imported and used in any component to share logic. For example, a useCounter composable might return a count ref and increment method.
Result
You can reuse logic and state across components by calling the composable function.
Seeing composables as simple functions that bundle reactive state and logic helps demystify their power and simplicity.
3
IntermediateCreating Reactive State Inside Composables
🤔Before reading on: do you think reactive() or ref() is better for simple numbers? Commit to your answer.
Concept: Use ref for simple values and reactive for objects inside composables to create reactive state.
Inside a composable, you can create reactive state like this: import { ref, reactive } from 'vue'; function useCounter() { const count = ref(0); // reactive number const user = reactive({ name: 'Alice' }); // reactive object function increment() { count.value++; } return { count, user, increment }; } This state updates automatically wherever used.
Result
The composable holds live reactive data that updates all components using it.
Choosing the right reactive API inside composables ensures efficient and clear state management.
4
IntermediateSharing Reactive State Across Components
🤔Before reading on: if two components use the same composable, do they share the same state or have separate copies? Commit to your answer.
Concept: By default, calling a composable creates a new reactive state instance for each component, but you can share state by defining it outside the function.
Example of separate state: import { ref } from 'vue'; function useCounter() { const count = ref(0); return { count }; } Each component calling useCounter() gets its own count. Example of shared state: import { ref } from 'vue'; const sharedCount = ref(0); function useSharedCounter() { return { count: sharedCount }; } Now all components share the same count.
Result
You control whether state is isolated or shared by where you declare reactive variables.
Knowing how to share or isolate reactive state in composables is key to designing predictable app behavior.
5
IntermediateUsing Computed and Watch Inside Composables
🤔Before reading on: can you use computed properties and watchers inside composables? Commit to your answer.
Concept: Composables can include computed properties and watchers to derive or react to state changes.
Inside a composable: import { ref, computed, watch } from 'vue'; function useCounter() { const count = ref(0); const double = computed(() => count.value * 2); watch(count, (newVal) => { console.log('Count changed to', newVal); }); function increment() { count.value++; } return { count, double, increment }; } This adds reactive derived data and side effects.
Result
Composables can react to and compute new values from reactive state, making them powerful logic containers.
Using computed and watch inside composables lets you encapsulate complex reactive behavior cleanly.
6
AdvancedHandling Lifecycle and Cleanup in Composables
🤔Before reading on: do you think composables can access component lifecycle hooks? Commit to your answer.
Concept: Composables can use Vue lifecycle hooks like onMounted and onUnmounted to manage side effects and cleanup.
Example: import { ref, onMounted, onUnmounted } from 'vue'; function useWindowWidth() { const width = ref(window.innerWidth); function updateWidth() { width.value = window.innerWidth; } onMounted(() => window.addEventListener('resize', updateWidth)); onUnmounted(() => window.removeEventListener('resize', updateWidth)); return { width }; } This composable tracks window width reactively and cleans up listeners.
Result
Composables can manage side effects safely tied to component lifecycles.
Lifecycle awareness in composables prevents memory leaks and unexpected bugs in real apps.
7
ExpertOptimizing Composables with Dependency Injection
🤔Before reading on: can composables accept parameters to customize their behavior? Commit to your answer.
Concept: Composables can accept parameters and dependencies to be more flexible and testable, enabling dependency injection patterns.
Example: import { ref } from 'vue'; function useApiClient(apiClient) { const data = ref(null); async function fetchData() { data.value = await apiClient.get('/items'); } return { data, fetchData }; } You can pass different apiClient implementations for testing or different environments. This pattern improves composable reusability and decouples logic from implementation details.
Result
Composable functions become highly flexible and easier to maintain or test.
Injecting dependencies into composables unlocks advanced patterns for scalable and testable Vue apps.
Under the Hood
Vue's reactivity system uses proxies to track when reactive data is accessed or changed. When a composable creates reactive state with ref or reactive, Vue wraps the data in proxies that notify dependent components to update automatically. Computed properties track their dependencies and recalculate only when needed. Watchers observe reactive changes and run side effects. Lifecycle hooks inside composables register callbacks tied to component lifecycles, ensuring proper setup and cleanup.
Why designed this way?
Vue's Composition API and composables were designed to solve the limitations of the Options API, which scattered related logic across component options. By using functions that return reactive state and logic, Vue enables better code reuse, organization, and type inference. The proxy-based reactivity system is efficient and intuitive, allowing automatic UI updates without manual DOM manipulation. This design balances developer ergonomics with performance.
┌───────────────────────────────┐
│ Vue Reactivity System          │
│ ┌───────────────┐             │
│ │ Proxy Wrapper │◄────────────┤
│ └───────────────┘             │
│         ▲                     │
│         │ Tracks dependencies  │
│ ┌───────────────┐             │
│ │ Composable    │             │
│ │ (ref/reactive)│             │
│ └───────────────┘             │
│         ▲                     │
│         │ Returns reactive state│
│ ┌───────────────┐             │
│ │ Vue Component │             │
│ └───────────────┘             │
└───────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does calling a composable multiple times share the same reactive state by default? Commit to yes or no.
Common Belief:Calling a composable multiple times always shares the same reactive state across components.
Tap to reveal reality
Reality:Each call to a composable creates a new reactive state instance unless the state is declared outside the function to be shared.
Why it matters:Assuming shared state by default can cause bugs where components do not sync as expected or accidentally share state when isolation was intended.
Quick: Can you mutate reactive state directly without .value when using ref? Commit to yes or no.
Common Belief:You can treat ref variables like normal variables and assign to them directly without .value.
Tap to reveal reality
Reality:Ref variables require accessing or assigning their value via the .value property to maintain reactivity.
Why it matters:Forgetting .value leads to bugs where UI does not update because Vue cannot track changes properly.
Quick: Do composables automatically clean up side effects when components unmount? Commit to yes or no.
Common Belief:Composables automatically clean up any side effects like event listeners when components unmount.
Tap to reveal reality
Reality:Composables must explicitly use lifecycle hooks like onUnmounted to clean up side effects; otherwise, leaks occur.
Why it matters:Ignoring cleanup causes memory leaks and unexpected behavior in long-running apps.
Quick: Is it best practice to put all app state inside composables? Commit to yes or no.
Common Belief:All application state should be managed inside composables for simplicity.
Tap to reveal reality
Reality:While composables are great for reusable logic, large or global state is better managed with dedicated state management libraries like Pinia.
Why it matters:Misusing composables for all state can lead to tangled code and poor scalability.
Expert Zone
1
Composables can be combined and nested to build complex reactive logic while keeping code modular and testable.
2
Reactive state inside composables can be proxied multiple times if not careful, causing subtle bugs; understanding Vue's reactivity internals helps avoid this.
3
Using dependency injection in composables allows swapping implementations for testing or environment-specific behavior without changing component code.
When NOT to use
Avoid using composables for managing large global state or complex cross-component communication; instead, use state management libraries like Pinia or Vuex. Also, do not use composables to replace simple local state that belongs only inside a single component.
Production Patterns
In production, composables are used to encapsulate API calls, form handling, authentication logic, and shared UI state. Teams often create libraries of composables for common tasks to ensure consistency and reduce bugs. Dependency injection patterns in composables enable easier unit testing by mocking dependencies.
Connections
Functional Programming
Composable functions in Vue follow the functional programming principle of building complex behavior by combining small pure functions.
Understanding composability in functional programming helps grasp how Vue composables promote modular, reusable, and testable code.
Observer Pattern
Vue's reactive system underlying composables implements the observer pattern, where reactive data notifies dependents on change.
Recognizing this pattern clarifies how Vue tracks dependencies and updates UI automatically.
Shared Workspace Collaboration
Sharing reactive state across components via composables is like multiple people collaborating on a shared document in real time.
This connection highlights the importance of managing shared state carefully to avoid conflicts and ensure consistency.
Common Pitfalls
#1Forgetting to use .value when accessing or updating ref variables.
Wrong approach:const count = ref(0); count = 5; // wrong, does not update reactive state
Correct approach:const count = ref(0); count.value = 5; // correct, updates reactive state
Root cause:Misunderstanding that ref wraps the value and requires .value to access or mutate.
#2Declaring reactive state inside composable but expecting it to be shared across components.
Wrong approach:function useSharedCounter() { const count = ref(0); return { count }; } // Each component gets separate count
Correct approach:const count = ref(0); function useSharedCounter() { return { count }; } // All components share the same count
Root cause:Not realizing that reactive state declared inside the function is recreated per call.
#3Not cleaning up side effects like event listeners in composables.
Wrong approach:function useResize() { window.addEventListener('resize', () => { /* update state */ }); } // No cleanup on component unmount
Correct approach:import { onUnmounted } from 'vue'; function useResize() { const handler = () => { /* update state */ }; window.addEventListener('resize', handler); onUnmounted(() => window.removeEventListener('resize', handler)); } // Proper cleanup
Root cause:Ignoring Vue lifecycle hooks inside composables leads to memory leaks.
Key Takeaways
Composables with reactive state are reusable functions that bundle live data and logic for Vue components.
Vue's reactivity system uses proxies to track and update UI automatically when reactive state changes.
By controlling where reactive state is declared, you decide if it is shared or isolated across components.
Lifecycle hooks inside composables help manage side effects and prevent memory leaks.
Advanced composables use dependency injection to increase flexibility and testability in real-world apps.