Literally Just How to Fetch Data in Vue3 Single File Component and Like Render It


I just started learning Vue3 by making a project where I want to fetch some data and render it (gasp! no way). It's easy to make a count increment with the basics of ref() and click handlers, but there were no simple examples of fetching some data, storing it to a reactive var, and rendering it. There are better ways to do this, but I learn best by getting something served and HOT reloading.

# how to

In SFCs you have to import the normal hooks that you have in Vue2:

<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'
</script>

Next, use defineComponent() which is a macro that can infer prop types and is the way to go if using TypeScript (I think).

export default defineComponent({
  setup() {
    // everything happens here
  }
})

In the body of setup you start by defining which reactive variables you want to assign your data to and eventually render. I am using homebrew's api, hence the names:

const brewData = ref<any[] | null>(null)
const loading = ref<boolean>(true)
const error = ref<string | null>(null)

Then, make a fetching fn like you would normally with one caveat. When assigning the data to the reactive ref you need to tack on .value:

 async function fetchAllFormulae(): Promise<void> {
   const res  = await fetch('https://formulae.brew.sh/api/formula.json')
   const body = await res.json()
   if (body) brewData.value = body // THE CAVEAT :warning:
}

Then in the mounted hook, call ur fn:

onMounted(async (): Promise<void> => await fetchAllFormulae())

And finally you have to return anything you want to use in the template:

return { brewData, loading, error }

Rendering is the same as in Vue2:

<div v-for="item in brewData">
 <Card :item="item" />
</div>

# Summary

  1. declare whatever the var the data will be assigned to using ref(null)
  2. put everything in a setup body
  3. when assigning use your data-variable-name + .value
  4. call it within an onMounted fn
  5. return it at the end of all this
  6. render as normal

# Full Example:

<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'

export default defineComponent({
  setup() {
    const brewData = ref<any[] | null>(null)
    const loading = ref<boolean>(true)
    const error = ref<string | null>(null)

    async function fetchAllFormulae(): Promise<void> {
      const res  = await fetch('https://formulae.brew.sh/api/formula.json')
      const body = await res.json()
      if (body) brewData.value = body
    }

    onMounted(async (): Promise<void> => await fetchAllFormulae())

    return { brewData, loading, error }
  }
})
</script>
<template>
  <div v-if="!loading && brewData && brewData.length > 0">
    <div v-for="item in brewData">
      <Card :item="item" />
    </div>

    <!-- use the loading, error variables here to provide a GORG ux for your u -->
  </div>
</template>