As a Vue.js developer, we want to keep our code clean and tidy by creating the optimal amount of reusable code.
In Vue 3 with the Composition API, we can absolutely do that by creating reusable JS modules aka hook functions and child components.
So what’s the difference between them?
When do we use one or the other?
Let’s find out!
JavaScript module aka hook function is a great way to separate business logic from the Vue component.
It’s completely reusable and UI independent.
If you’re coming from MVC pattern, this would be M (Model) part of it.
Vue components that are not page-based, meaning no route attached to it, aka Child components, are great when we want to reuse HTML template code aka UI that can be used in multiple parent components.
Now you have a better understanding of not only the differences, but also which one to use when.
I’m going to demonstrate with some example code throughout this article to show cases of when to use them.
Note: This article is intermediate and you should have the basic knowledge of using Vue.js or a similar framework.
Get A List Of Products
I assume you already know how to get Up and Running With Vue JS 3 Project Using Vue CLI.
Let’s say we have a ProductList.vue page-based component in our application handling async operation to get a list of products.
I used Firebase in the example below for the async operations.
I have an article on how to install Firebase to your vue project as well as on how to make Cloud Firestore queries if you’re interested.
First, create a product reactive array and call loadProduct() function inside onMounted() lifecycle method.
Recommended
Must-Know Ref vs Reactive Differences In Vue 3 Composition API
import firebase from "firebase";
import { onMounted, reactive } from "vue";
export default {
setup() {
const products = reactive([]);
onMounted(() => {
loadProducts();
});
const loadProducts = async () {
try {
const productsSnap = await firebase
.firestore()
.collection("products")
.orderBy("brand", "asc")
.get();
productsSnap.forEach((doc) => {
let product = doc.data();
product.id = doc.id;
products.push(product);
});
} catch (e) {
console.log("Error Loading Products");
}
}
return {
products,
};
},
};
Inside the loadProducts() declaration, use Firebase query to get a list of products that I’ve already stored in the Firebase Cloud Firestore database.
Finally return the products array to use in the template.
Each product object has two keys that are:
- title
- upc
Nothing fancy.
This works great!
As the project evolves, let’s say I’ll need to create an inventory component where I want to use a list of products as a drop-down, for example, to select a product to create an inventory from.
I’ll have to rewrite loadProducts() code which I do not want to do.
With Vue 3 Composition API, we can easily extract the async code into a UI-independent JavaScript module and can reuse it wherever we want in our application.
Let’s see how to do that.
Reusable Product.js Module
Create a Product.js file inside the src/modules folder which is where all of my product-related async operations will go such as loadProducts, addProduct, updateProduct, deleteProduct, etc.
Let’s see how to extract the loadProducts() code from the ProductList.vue to Product.js.
Import Firebase as we’ll be using it for CRUD async operations.
Then declare useProduct() function as well as reactive state object inside the function which will have one property in it called products that is a type of array.
import { reactive, toRefs } from "vue";
import firebase from "firebase";
export default function useProduct() {
const state = reactive({
products: [],
});
const loadProducts = async() => {
try {
const productsSnap = await firebase.firestore().collection("products").orderBy("brand", "asc").get();
productsSnap.forEach(doc => {
let product = doc.data();
product.id = doc.id;
state.products.push(product);
});
} catch (e) {
console.log("Error Loading Products")
}
}
return { ...toRefs(state),
loadProducts
}
}
Then, create the loadProducts() function which gets the products data from the products collection in Cloud Firestore and gets the snapshot of it.
Loop through them and push each product into the products array.
Finally, return the state and loadProduct.
The reason I wrapped state with toRefs is to convert the reactive object into a plain JavaScript object so that I can destructure it when needed.
Now we have the Product.js module ready to use inside the ProductList.vue or any component that needs a list of products in our app.
ProductList.vue
Now, we can easily get a list of products like before with just four steps.
First, import the product module at the top.
Secondly, call useProduct() function and destructure products and loadProducts inside the setup() function.
Then, invoke loadProducts() inside the onMounted() lifecycle method.
import { onMounted } from "vue";
import useProduct from "@/modules/product";
export default {
setup() {
const { products, loadProducts } = useProduct();
onMounted(async () => {
await loadProducts();
});
return {
products,
};
},
};
Finally, return the products which is the same as before.
At this stage, products are available in the template to use and we can loop through them using v-for to show however we want.
Recommended
Vue + Firestore ← Build A Simple CRUD App with Authentication
You can see how easy it is to create a module like Product.js to organize reusable UI-independent functionalities. 🙂
One more benefit of using modules like Product.js is to use them as state management as well.
Modules As State Management
To use modules as state management, all we need to do is to declare the state object model outside of the useProduct() function which then becomes a global variable.
import { reactive, toRefs } from "vue";
import firebase from "firebase";
const state = reactive({
products: [],
});
export default function useProduct() {
...
}
Top comments (0)