Stateful Logic Reuse ft. Composables
"Argh... The duplication. Let's remove it" - Says every software developer.
Code reuse is arguably one of the most satisfying experiences of a developer's life. Since it leads to lesser code to manage!
Composables enable us to reuse stateful logic
"Stateful" - What's that. We will see.
Laying the foundations ft. Code Reuse
function fetchPosts() {
const baseURL = import.meta.env.VITE_API_BASE_URL
const url = `${baseUrl}/api/posts`;
fetch(url).then(response => console.log(response))
}
function fetchUsers() {
const baseUrl = import.meta.env.VITE_API_BASE_URL
const url = `${baseUrl}/api/posts`;
fetch(url).then(response => console.log(response))
}
Minor Refactor
function getApiBaseUrl() {
return import.meta.env.VITE_API_BASE_URL
}
function fetchPosts() {
const baseURL = getApiBaseUrl()
...
}
function fetchUsers() {
const baseUrl = getApiBaseUrl();
...
}
Now the logic for getting Base Api Url is reusable. You do this all the time, you see some piece of code that is getting duplicated, you move it to a function and make it reusable. Common examples include formatter functions, data transformers. You get the idea
This getting the base API url, formatter functions are an example of stateless logic reuse.
Stateful Logic Reuse
Let's say we want to reuse a piece of code that is dependent on the state? State changes the output should change. And this state is needed in various components through out your application. Composable are our best bet!
Address
Let's say you have a form part of which is to take user's address. You have fields to get data for street, city, district, country.
User's Address
// UserAddress.vue
<script setup>
import { ref } from 'vue'
const address = ref({
street: '',
city: '',
district: '',
country: ''
})
</script>
<template>
// user info form
</template>
For example, you now have to have address for a Company (just another entity within your application)
CompanyAddress
// CompanyAddress.vue
<script setup>
import { ref } from 'vue'
const address = ref({
street: '',
city: '',
district: '',
country: ''
})
</script>
<template>
// Company Info Form
</template>
Reusing the Address Info Logic
Address Information is stateful logic, It changes as the user types
User's Address & Company Address share the same info and we can reuse it. We will create a new file called useAddressInfo.js (you can name it whatever you want) but the convention is to start the composable names with "use" .
// useAddress.js
import { ref, toValue } from 'vue'
const DEFAULT_ADDRESS_DATA = {
street: '',
city: '',
district: '',
country: ''
}
export const useAddress = (initialData = null) => {
const data = ref(initialData ?? DEFAULT_ADDRESS_DATA)
const setAddress = (updatedData) => {
data.value = { ...toValue(updatedData) }
}
// availabe in our components
return {
data, // notice this is a ref
setAddress
}
}
Now let's refactor our UserAddress & CompanyAddress components
UserAddress.vue
// UserAddress.vue
<script setup>
import { ref } from 'vue'
import { useAddress } from 'useAddress'
// No need to manage the address ref here anymore.
const { data: address, setAddress } = useAddress();
</script>
<template>
// User Info Form
</template>
CompanyAddress.js
// CompanyAddress.vue
<script setup>
import { ref } from 'vue'
import { useAddress } from 'useAddress'
// No need to manage the address ref here anymore.
const { data: address, setAddress } = useAddress();
</script>
<template>
// Company Info Form
</template>
Multiple Locations? Not a problem!
Let's say your company has various locations i.e Head Office, warehouse, customer care.
You can just rename in your local scope while destructuring & you are good to go! Each instance get's it's own copy of reactive data!
// CompanyAddress.vue
<script setup>
import { ref } from 'vue'
import { useAddress } from 'useAddress'
// various type of addresses
const { data: headOfficeAddress, setAddress: setHeadOfficeAddress } = useAddress();
const { data: warehouseAddress, setAddress: setWarehouseAddress } = useAddress();
const { data: customerCareAddress, setAddress: setCustomerCareAddress } = useAddress();
</script>
<template>
// Company Info Form
</template>
Hope it helped!
Subscribe to my newsletter
Read articles from Nauman Zafar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Nauman Zafar
Nauman Zafar
Software Engineer, all-willing to unlearn and relearn!