Shared State
So far, data that several components needed lived in their parent: it passed the data down with props, and the children asked for changes with events, as in Component Events. That's the right way for a parent and its children. But some data is needed all over an app: the order, the signed-in user, the cart of a shop. Passing it down through every component in between gets long.
There's a simpler way: put the ref in a module of its own, outside any function, with the functions that change it:
import { ref } from "vue";
export const order = ref([]);
export function addToOrder(item) {
const found = order.value.find((line) => line.id === item.id);
if (found) {
found.count++;
} else {
order.value.push({ ...item, count: 1 });
}
}Each component that needs the order imports what it uses:
<script setup>
import { order } from "./order.js";
</script>
<template>
<p>Items ordered: {{ order.length }}</p>
</template>This works because a module runs only once. The first import runs order.js and creates the ref; every other import gets that same ref. So when MenuItem.vue calls addToOrder(), it adds to the very order that OrderList.vue shows, and Vue updates the list, as after any change of a ref.
Compare it with a composable: there, ref() is inside the function, so each call creates a new ref, and each component has its own. Here, ref() is outside, so there's only one, which all components share. The same line in another place makes the difference.
Keep the functions that change the shared state next to it, in the same module, as addToOrder() is. Then you always know where the order changes: wherever it changes, it's through one of them.
In bigger apps, shared state usually goes into Pinia, a library from the Vue team that works on the same idea and adds tools for finding mistakes. You'll meet it after this course.
Instructions
- 1
Make the
Orderbuttons work. InMenuItem.vue, importaddToOrderfrom./order.js, and call it with the item when the button is clicked. InOrderList.vue, importorderfrom./order.jsin place of the empty array. Order an espresso twice: the list says2 × Espresso. - 2
Add a way to remove a line. In
order.js, write and exportremoveFromOrder(id), which keeps only the lines with anotherid, withfilter(). InOrderList.vue, add a button×to each line that calls it. - 3
Show the order's total in
App.vue, under the heading:Total: €8. Inorder.js, export a computed valuetotalthat adds upprice * countof all the lines with a loop, and import it inApp.vue.