Composables
Prerequisites
Composables use the Composition API introduced in Vue 3 but also made available via plugins in Vue 2. If you are unfamiliar with it, see the official Composition API FAQ (opens new window).
What are composables?
Composables are functions with an internal state that changes over time and methods that modify this state. You cannot directly modify the state. The only way to change the state is by calling one of the composable's methods. However, because the state is reactive — thanks to Vue's Composition API — you can watch and react to these changes when necessary to update the UI or perform other operations.
This pattern encapsulates the state and business logic and exposes it through easy-to-use methods.
Anatomy of a composable
Most composables consist of one or more of the following:
- Primary state - read-only state of the composable, which you cannot update directly.
- Supportive state - additional read-only state for values such as the status of the requests or errors.
- Methods - functions that update the primary and supportive states. These methods usually call API endpoints but can also manage cookies or call methods from other composables.
To make composables easily distinguishable from standard methods, we follow the popular convention of names starting with "use".
What does it look like in practice?
Let's take a closer look at how it might look like using the useUser composable as an example:
In this example:
- the
user
property is the primary state, - the
loading
anderror
properties represent the supportive state, - the
load
,register
,login
,logout
,changePassword
, andupdateUser
are methods.
Usage
Let's see how you can use the useUser composable to load the current user's data:
<script>
import { useUser } from '{INTEGRATION}';
import { useFetch } from '@nuxtjs/composition-api';
export default {
setup() {
/**
* Extract needed methods and state variables from the composable
*/
const { load, user } = useUser();
/**
* Load user data. The result will update the `user` object.
*/
useFetch(async () => {
await load();
});
/**
* Return the `user` object to make it available in the template
*/
return {
user
};
}
};
</script>
While it's okay to destructure a composable like we did above, you should not destructure read-only states, such as the user
or error
properties. Doing it this way will create variables that are not reactive and don't update.
/**
* ❌
* Destructuring `user` will create variables that
* aren't reactive and don't update
*/
const { user: { value: { firstname } } } = useUser();
/**
* ✔️
* Using `computed` will make the variable react to
* changes in the `user` object
*/
const { user } = useUser();
const firstname = computed(() => user.value.firstname);
This raises two questions:
- What is the
useFetch
, and what does it do? - What happened when we called the
load
method?
useFetch
and other hooks for fetching data
There are many hooks available in Composition API, but let's only focus on the most common ones used for fetching data:
- The useFetch (opens new window) and useAsync (opens new window) are Nuxt-specific hooks called on the server-side when rendering the route and on the client-side when navigating between pages.
- The
onMounted
is a lifecycle hook called only on the client-side after the browser loads the page. - The
onServerPrefetch
is a lifecycle hook called only on the server-side when rendering the route.
You can use one or more hooks simultaneously, even in the same component.
Internals of the load
method
You might be wondering what happened within the composable when we called the load
method in the example above. The behavior of methods is different between composables. Still, in the case of the useUser
composable, the load
method updated the loading
, error
, and user
properties to reflect the current state, made an API call, and then updated the state with the API's response.
- It set the
loading
property totrue
. - It called the corresponding API endpoint to load the current user's data.
- if the request succeeded, it updated the
user
property, - otherwise, it added the error message to the
error
property.
- if the request succeeded, it updated the
- It set the
loading
property tofalse
.