Claude
Skills
Sign in
Back

composables

Included with Lifetime
$97 forever

Teaches Vue composables for reusable stateful logic with the Composition API. Use when extracting shared reactive state, side effects, or computed logic that multiple components need.

Web Dev

What this skill does


# Composables

## Table of Contents

- [When to Use](#when-to-use)
- [Instructions](#instructions)
- [Details](#details)
- [Source](#source)

Composables are functions that encapsulate and reuse stateful logic using Vue's Composition API. They replace the Options API's fragmented code organization with clean, composable functions that can be shared across components.

## When to Use

- Use this when you need to share stateful logic across multiple components without duplication
- This is helpful for extracting concerns like data fetching, event listeners, or timers into reusable functions

## Instructions

- Name composable functions with the `use` prefix by convention (e.g., `useCounter`, `useWidth`)
- Use `ref()`, `reactive()`, and lifecycle hooks (`onMounted`, `onBeforeUnmount`) inside composables
- Return reactive state and methods from composables for use in component templates
- Prefer composables over the Options API for better code organization, reusability, and TypeScript support

## Details

### Options API

Before the introduction of the Composition API in Vue, developers relied on the **Options API** to organize component logic which include reactive data, lifecycle methods, computed properties, and more. The Options API allowed defining these aspects within specific options, as shown in the example below:

```html
<!-- Template -->

<script>
  export default {
    name: "MyComponent",
    props: {
      // props
    },
    data() {
      // data
    },
    computed: {
      // computed properties
    },
    watch: {
      // properties to watch
    },
    methods: {
      // methods
    },
    created() {
      // lifecyle methods like created
    },
    // ...
  };
</script>

<!-- Styles -->
```

While this approach served its purpose and is still applicable in Vue v3, it can become challenging to manage and maintain as components grow larger and more complex. Defining component logic within specific options can make it harder to read and understand the code, especially when dealing with extensive components. Extracting and reusing common logic between components can also be difficult in this setup.

Let's take a look at a simple example of an `App` component that renders two individual child components — `Count` and `Width`.

```html
<template>
  <div class="App">
    <Count :count="count" :increment="increment" :decrement="decrement" />
    <div id="divider" />
    <Width :width="width" />
  </div>
</template>

<script>
  import Count from "./components/Count.vue";
  import Width from "./components/Width.vue";

  export default {
    name: "App",
    data() {
      return {
        count: 0,
        width: 0,
      };
    },
    mounted() {
      this.handleResize();
      window.addEventListener("resize", this.handleResize);
    },
    beforeUnmount() {
      window.removeEventListener("resize", this.handleResize);
    },
    methods: {
      increment() {
        this.count++;
      },
      decrement() {
        this.count--;
      },
      handleResize() {
        this.width = window.innerWidth;
      },
    },
    components: {
      Count,
      Width,
    },
  };
</script>
```

The code snippet above represents a Vue single-file component (SFC) named `App`.

The `<template>` section defines the markup of the component. In this case, it contains a `<div>` element with the class "App" that wraps two child components: `<Count>` and `<Width>`. These child components are passed certain properties using Vue's attribute binding syntax (`:count`, `:increment`, `:decrement`, and `:width`).

The `<script>` section contains the JavaScript code for the component. Within the component definition, we have:

- The `data` method which returns an object containing the initial data properties of the component, which are `count` and `width` initialized to 0.
- The `mounted()` lifecycle hook is used to execute code after the component has been mounted in the DOM. In this case, it calls the `handleResize()` method and adds an event listener for the resize event.
- The `beforeUnmount()` lifecycle hook is used to execute code before the component is unmounted and destroyed. Here, it removes the event listener for the resize event.
- The `methods` object contains the component's methods. It defines `increment()`, `decrement()`, and `handleResize()` methods that manipulate the count and width data properties based on certain events or actions.

When the app is run, the current count and the window's inner width are displayed in real-time. The user can interact with the component by incrementing and decrementing the count using the buttons in the `<Count>` component. Similarly, the width is automatically updated whenever the window is resized.

Even though this component is small in size, the logic inside it is already intertwined. Some parts are dedicated to the functionality of the counter, while others pertain to the width logic. As the component grows, organizing and locating related logic within the component would become more challenging.

To address these challenges, the Vue team introduced the Composition API in Vue v3.

### Composition API

The Composition API can be seen as an **API that provides standalone functions representing Vue's core capabilities**. These functions are primarily used within a single `setup()` option which serves as the entry point for utilizing the Composition API.

```html
<!-- Template -->

<script>
  export default {
    name: "MyComponent",
    setup() {
      // the setup function
    },
  };
</script>

<!-- Styles -->
```

The `setup()` function is executed before a component is created and when the props of the component are available.

With the Composition API, we can import standalone functions to help us access Vue's core capabilities within our component. Let's rewrite the counter and width example we've seen above while relying on the Composition API syntax.

```html
<template>
  <div class="App">
    <Count :count="count" :increment="increment" :decrement="decrement" />
    <div id="divider" />
    <Width :width="width" />
  </div>
</template>

<script>
  import { ref, onMounted, onBeforeUnmount } from "vue";
  import Count from "./components/Count.vue";
  import Width from "./components/Width.vue";

  export default {
    name: "App",
    setup() {
      const count = ref(0);
      const width = ref(0);

      const increment = () => {
        count.value++;
      };

      const decrement = () => {
        count.value--;
      };

      const handleResize = () => {
        width.value = window.innerWidth;
      };

      onMounted(() => {
        handleResize();
        window.addEventListener("resize", handleResize);
      });

      onBeforeUnmount(() => {
        window.removeEventListener("resize", handleResize);
      });

      return {
        count,
        width,
        increment,
        decrement,
      };
    },
    components: {
      Count,
      Width,
    },
  };
</script>
```

The `<template>` of our component remains the same but in the `<script>` section of our component, we now utilize the Composition API with the `setup()` function.

Inside the `setup()` function, we:

- Define the `count` and `width` reactive variables using the `ref()` function — the function that accepts a single primitive value (e.g. string, number, etc.) and returns a reactive/mutable object.
- We also define the custom functions `increment()`, `decrement()`, and `handleResize()`. These functions are similar to the methods we defined in our previous Options API example.
- We use the `onMounted()` lifecycle function to call the custom `handleResize()` function and add an event listener for the resize event when the component is mounted. Similarly, we use the `onBeforeUnmount()` lifecycle function to remove the event listener for the resize event before the component is unmounted.
- The reactive variables and functions defined in the `setup()` function are then returned, making them accessible in the component 
Files: 1
Size: 12.8 KB
Complexity: 21/100
Category: Web Dev

Related in Web Dev