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!