5
votes

I've installed Vue 3 and want to populate a reactive object with fetch from a JSON API.

My component looks like below. I don't get any errors but I don't get any results either.

<template>
  <div>
    {{ state.test.total }}
  </div>
</template>

<script>
import { reactive } from "vue";

export default {
  setup() {
    const state = reactive({
      test: null,
    });

    state.test = async () => {
      return await fetch("https://api.npms.io/v2/search?q=vue");
    };

    return {
      state,
    };
  },
};
</script>

I expected to get a number on the screen because that's what's in total in the JSON file.

On state.test

If I only output state.test I get the output below.

function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }

Any idea how I can get around it?

3

3 Answers

7
votes

When you do:

a = async function test() {
}

You're assigning a function to your state. But if you you do

a = (async function test() {
})();

You're still assigning a promise to a, rather than a value. If you want to assign a value you need to resolve this promise:

funcResult = await a;

Your setup function is not a proper place for code that should be executed during lifetime of your component. You can think of setup function as a factory for your component. Thus setup function should always be synchronous (no async keywrord), otherwise vue would not know what it should display while setup is resolving. Luckily you can use hooks in composition API:

import { onMounted, reactive } from "vue";

export default {
  setup() {
    const state = reactive({
      test: null,
    });


    onMounted(async () => {
      const response = await fetch("https://api.npms.io/v2/search?q=vue");
      state.test = await response.json();
    });


    return {
      state,
    };
  },
};

EDIT

Taking into account @boussadjra-brahim answer, you can actually define async setup function, but only in case if you wrap your component with <Suspense>. So you can either go with this or that variant.

4
votes

I think it's better not to wait.

Example:

<template>
    <div>
        {{ state }}
    </div>
</template>


<script>
import { ref } from 'vue';

export default {
    setup() {
        const state = ref({});

        fetch('https://api.npms.io/v2/search?q=vue')
            .then(response => response.json())
            .then(data => state.value = data);

        return { state };
    },
};
</script>
0
votes

You should add async to setup option and await to the fetch function and wrap your template content by Suspense component:

<template>
   <Suspense>
    <div>
      {{ state.test.total }}
    </div>
  </Suspense>
</template>

<script>
import { reactive } from "vue";
export default {
  async setup() {
    const state = reactive({
      test: null,
    });

    try{
      state.test =  await fetch("https://api.npms.io/v2/search?q=vue");
     } catch (err) {
       console.error(err)
   }

    return {
      state,
    };
  },
};