Retour au code
Vue 3TypeScript

Extraire le composable avant que le composant ait deux métiers

Un composant qui récupère les données, les filtre, et les affiche fait trois métiers. Un seul est le métier d'un composant.

Mauvais - récupération, état et template fusionnés

<script setup lang="ts">
import { ref, onMounted, computed } from "vue";

const invoices = ref<Invoice[]>([]);
const loading = ref(false);
const query = ref("");

onMounted(async () => {
  loading.value = true;
  const res = await fetch("/api/invoices");
  invoices.value = await res.json();
  loading.value = false;
});

const filtered = computed(() =>
  invoices.value.filter((i) => i.client.includes(query.value))
);
</script>

Rien ici n'est réutilisable, et rien n'est testable sans monter le composant. La logique de récupération et la logique de filtrage ne peuvent pas être raisonnées, ni réutilisées, indépendamment du template.

Bon - le composant ne fait qu'afficher

// composables/useInvoices.ts
export function useInvoices() {
  const invoices = ref<Invoice[]>([]);
  const loading = ref(false);

  async function load() {
    loading.value = true;
    invoices.value = await api.get<Invoice[]>("/invoices");
    loading.value = false;
  }

  onMounted(load);

  return { invoices, loading, reload: load };
}
<script setup lang="ts">
const { invoices, loading } = useInvoices();
const query = ref("");
const filtered = computed(() =>
  invoices.value.filter((i) => i.client.includes(query.value))
);
</script>

useInvoices est testable en isolation sans DOM, réutilisable dans une deuxième vue, et le composant ne fait plus qu'un seul métier : transformer l'état en balisage.

Le compromis : un fichier et un niveau d'indirection en plus pour ce qui était en ligne auparavant. Ça ne vaut pas le coup pour une page ponctuelle ; ça vaut le coup dès que deux composants ont besoin des mêmes données.