Blog

Next generation async functions with Vue Async Function

12 Jul, 2019
Xebia Background Header Wave

Vue Async Function
At the last Xebia Knowledge Exchange event we experimented with the Vue Function API and created vue-async-function, which is available for your Vue projects right now! Let’s dive in and see what it brings to the table.

Background of Vue Async Function

A while ago the React team released React Hooks. It’s a new way of managing state in your React components without the traditional class syntax. The Vue.js team wants to introduce a similar syntax for their next major version of Vue. They created a proposal/open discussion document and are working on an implementation that is kept in sync with the proposal. Even though it is still under heavy discussion the implementation is available as a plugin for Vue 2.
Our goal during Xebia Knowledge Exchange events is to share knowledge and inspire others. Luckily we were joined by Gert, the creator of React Async who thought it would be a nice idea to re-implement the useAsync and useFetch hook from his library. We went ahead and within an hour we succeeded in re-implementing a basic version of both functions.

Development process

After you completed the installation instructions, let’s create a new component in the Single File Component format:

<template>
  <h2>useAsync example</h2>
</template>
<script>
export default {};
</script>

Now, we’ll introduce an asynchronous function that we want to call:

// ... template
<script>
async function loadStarship({ id }) {
  const headers = { Accept: "application/json" };
  const res = await fetch(https://swapi.co/api/starships/${id}/, { headers });
  if (!res.ok) throw new Error(res);
  return res.json();
}
export default {};
</script>

The function loadSpaceship will fetch some data from the Star Wars API. If the fetch is successful it will return the data as JSON, else it will throw.
Now, we want to:

  • call this function on component creation
  • show a loading indicator
  • present the data on data availability
  • render an error placeholder when the fetch failed for some reason

Without the Vue Function API you would now need to add:

  • a data method to prepare loading, error and data reactive properties
  • a created lifecycle method to call this function which updates the reactive properties

Over time, these would probably mix with other lifecycle and data concerns. This makes your code harder to understand and makes it less reusable. Of course, you already have certain design patterns to separate these concerns, for instance via mix-ins or inheritance. The Vue Function API gives you a new way to separate these concerns in an intuitive fashion. Let’s see how this could work:

<template>
  <div>
    <h2>useAsync example</h2>
    <div v-if="isLoading">Loading...</div>
    <div v-else-if="error">Error!</div>
    <div v-else>{{ data }}</div>
  </div>
</template>
<script>
import { value, onCreated } from "vue-function-api";
// ... same loadStarship function
export default {
  setup() {
    const isLoading = value(true);
    const error = value(null);
    const data = value(null);
    onCreated(async () => {
      try {
        const result = await loadStarship({ id: 2 });
        data.value = result;
      } catch (e) {
        error.value = e;
      } finally {
        isLoading.value = false;
      }
    });
    return {
      isLoading,
      error,
      data,
    };
  }
};
</script>

We can extend this example with error handling and use other lifecycle methods to make it even nicer. However, that’s not the point of the Vue Function API. In our setup function we now group our data and lifecycle concerns together. And even better, we can put these in separate functions. This is the preferred way to separate the different concerns, and this is where vue-function-apicomes in.

useAsync and useFetch

Let’s see how this example looks when you use useAsync:

// ... same template
<script>
import { useAsync } from "vue-async-function";
// ... same loadStarship function
export default {
  setup() {
    const { data, error, isLoading } = useAsync(loadStarship, { id: 2 });
    return { data, error, isLoading };
  }
};
</script>

That’s really short! Let’s make it even shorter. Fetching data is so common that there’s a dedicated useFetch function for it:

// ... same template
<script>
import { useFetch } from "vue-async-function";
export default {
  setup() {
    const id = 2;
    const { data, error, isLoading } = useFetch(https://swapi.co/api/starships/${id}/, {
      headers: { Accept: "application/json" }
    });
    return { data, error, isLoading };
  }
};
</script>

Bonus: when you use useAsync you also receive a AbortController.signal that you can hook up to abort network requests when the component is destroyed. If you use useFetch this is already taken care of.

Conclusion

The new Vue Function API brings a well-thought-out design pattern to the table. Writing reusable Vue functionality feel more intuitive and you can easily share it with others. Developing a useAsync and useFetch helper function went very smooth.
See the vue-async-function repo for the code or install the npm package. See more examples in the example repo.

About the author

Hi, I’m Albert Brand and I’m a software tinkerer and trainer at Xebia. Feel free to hook up!

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts