adeab
adeab

Reputation: 229

How to prevent template errors while fetching data with Nuxt's fetch() hook

I am trying to convert my vue app to nuxt app. In my vue app I used vue-slick-corousel with ease. But the same code with necessary modifications for nuxt, the corousel is not working on refresh. If I edit the template real time or navigate the app without refreshing the page, it works perfectly on second time visiting the page(s). Here is how I used vue-slick-corousel in my nuxt app

//nuxt.config.js
plugins: [
  { src: './plugins/vue-slick-carousel.js' }
],

Code in the plugin js file:

//vue-slick-carousel.js
import Vue from 'vue'
import VueSlickCarousel from 'vue-slick-carousel'

Vue.component('VueSlickCarousel', VueSlickCarousel)

And finally in the template:

<!-- CustomComponent.vue -->
<template>
  <div>
    <VueSlickCarousel :arrows="true" :dots="true">
      <div v-for="(cat, index) in categories" 
        :key="index"
        style="height: 20px; width: 10px; background-color: red; color: white"
      >
        {{cat.name}}
      </div>
    </VueSlickCarousel>
  </div>
</template>



<script>
  import VueSlickCarousel from 'vue-slick-carousel'
  import 'vue-slick-carousel/dist/vue-slick-carousel.css'
  // optional style for arrows & dots
  import 'vue-slick-carousel/dist/vue-slick-carousel-theme.css'
  export default {

    name: 'CustomComponent',
    data() {
      return {
        categories: []
      }
    },
    async fetch() {
      this.categories = await fetch(
        'https://*****************************/categories?limit=20'
      ).then(res => res.json())
    },
    components: {
      VueSlickCarousel
    },
  }
</script>

This is the error I am getting on refresh "TypeError: Cannot read property 'length' of undefined"

And whenever I am rendering the page again from clientside, the error goes away. Already tried bounding the container under

<client-only>

tried using a condition

v-if="slik.length" 

didn't work. How can I solve this issue?

Upvotes: 0

Views: 2018

Answers (1)

kissu
kissu

Reputation: 46696

EDIT: here is an explanation of the usage of $fetchState.pending helper.

  • fetch() is a non-blocking hook that will allow you to fetch data upon page/component loading
    • asyncData() is blocking but only when you navigate and not on initial render which can be annoying
    • you can make a blocking navigation with a combo of fetch() and a middleware
    • using skeletons is a nice way of having the user wait while feeling that the app is loading (Facebook-y)
  • your template is actually synchronous and will not wait for you to gather data, it is expecting to already have it right away. VueSlickCarousel is actually waiting for you to provide it an array of items. But upon initial render and before have fetched any data, you only do have categories: [] as setup in data()
  • $fetchState.pending is a handy helper that can tell us when the whole calls in fetch() are done. So if you will call 4 different APIs, it will equal to false only when all of the 4 calls will be done.
    • you can actually set it manually too with this.$fetchState.pending = true but this is forcing it and should be used when understood properly.
  • by writting <VueSlickCarousel v-if="!$fetchState.pending">, we do basically say wait until all of my things into fetch() to be done before mounting this component at all. This prevents any .length method issues because the component itself is not mounted until there is something to apply .length on.

Here is a small image of Nuxt's lifecycle, focused on fetch() hook. enter image description here


You should not mix async/await and then. Try to only use one of them (I do recommend the first one).

async fetch() {
  const response = await fetch(
    'https://*****************************/categories?limit=20'
  )
  const fetchedCategories = await response.json()
  console.log('categories', fetchedCategories)
  this.categories = fetchedCategories
},

Also, :key="index" is a bad practice because index is mutable in a v-for. Try to use an UUID from the API instead.

I'm pretty sure that those are not required neither:

  • import VueSlickCarousel from 'vue-slick-carousel'
  • components: { VueSlickCarousel },

Upvotes: 1

Related Questions