Vue

Lesson 06

Composables and state ownership

Use composables to group reusable reactive behavior while keeping each caller's state isolated.

Good Code

useReviewFilters.ts
import { computed, ref } from "vue";

export function useReviewFilters(reviews: Ref<Review[]>) {
  // Each caller receives its own filter refs.
  const query = ref("");
  const activeOnly = ref(true);

  const visibleReviews = computed(() =>
    reviews.value.filter((review) => {
      return (!activeOnly.value || review.active) &&
        review.title.includes(query.value);
    }),
  );

  return { query, activeOnly, visibleReviews };
}

Bad Code

reviewFilters.ts
import { computed, ref } from "vue";

// Module state is shared by every component that imports this file.
export const query = ref("");
export const activeOnly = ref(true);

export function visibleReviews(reviews: Ref<Review[]>) {
  return computed(() =>
    reviews.value.filter((review) => review.title.includes(query.value)),
  );
}

Review Notes

What to review

Good Code

The good version creates filter state inside the composable call. Two pages can use the same composable without sharing query text.

Bad Code

The bad version exports mutable refs from module scope. Components that import the module can overwrite each other's filters.

Takeaways

  • A composable should return the state and actions one caller needs, without sharing mutable module state by accident.