Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unhandled error during execution of scheduler flush. This is likely a Vue internals bug

I am getting the following error while creating slides in Vue.js:

[Vue warn]: Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next 
  at <Anonymous key=1 > 
  at <Anonymous pager="true" options= {initialSlide: 1, speed: 400} > 
  at <Anonymous fullscreen=true > 
  at <IonPage isInOutlet=true registerIonPage=fn<registerIonPage> > 
  at <Product Details ref=Ref< Proxy {…} > key="/products/1" isInOutlet=true  ... > 
  at <IonRouterOutlet> 
  at <IonApp> 
  at <App>
Uncaught (in promise) DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
    at insert (webpack-internal:///./node_modules/@vue/runtime-dom/dist/runtime-dom.esm-bundler.js:222:16)
    at mountElement (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:3958:9)
    at processElement (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:3899:13)
    at patch (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:3819:21)
    at componentEffect (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:4312:21)
    at reactiveEffect (webpack-internal:///./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js:71:24)
    at effect (webpack-internal:///./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js:46:9)
    at setupRenderEffect (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:4277:89)
    at mountComponent (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:4235:9)
    at processComponent (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:4195:17)

If I add the slides hard coded it does not show any errors. But if I add the slides dynamically using a v-for loop then it shows the above errors.

I have added the slides in following way:

This is the template:

  <ion-slides pager="true" :options="slideOpts">
    <ion-slide v-for="image in product.product_images" v-bind:key="image.id">
      <h1>Slide 1</h1>
    </ion-slide>
  </ion-slides>

This is the script:

export default {
  name: "Product Details",
  components: {
    IonContent,
    IonHeader,
    IonPage,
    IonTitle,
    IonToolbar,
    IonSlides,
    IonSlide,
  },
  data() {
    return {
      product: {},
    };
  },
  setup() {
    // Optional parameters to pass to the swiper instance. See http://idangero.us/swiper/api/ for valid options.
    const slideOpts = {
      initialSlide: 1,
      speed: 400,
    };
    return { slideOpts };
  },
  mounted: function () {
    fetch("http://localhost:4000/api/products/" + this.$route.params.id, {
      method: "get",
    })
      .then((response) => {
        return response.json();
      })
      .then((jsonData) => {
        this.product = jsonData;
        // console.log(jsonData.product_images);
      });
  },
};

What am I doing wrong in the code?

like image 599
Hello World Avatar asked Jan 21 '21 16:01

Hello World


1 Answers

Arguably, the error message could be improved on this one.

The error was caused by trying to iterate through a non-iterable (in your case undefined), using v-for. Specifically, before the call made in mount() returns, product.product_images is undefined, because you initiate product as empty object.

Vue 2 style solutions

  • instantiate product.product_image as iterable:
//...
  data: () => ({ 
    product: { product_images: [] }
  })

or provide an empty array as fallback in template:

<ion-slide v-for="image in product.product_images || []" v-bind:key="image.id">
  <h1>Slide 1</h1>
</ion-slide> 

or place a v-if on the parent of the v-for:

<ion-slides pager="true" :options="slideOpts" v-if="product.product_images">
  ...
</ion-slides>

Vue 3 style solution

Make the entire ProductDetails component suspensibe, by giving it an async setup function. In the setup function, make the call to get the product.
Proof of concept:

//...
async setup() {
  const product = await fetch("http://localhost:4000/api/products/" + 
    this.$route.params.id, {
      method: "get",
    }).then(r => r.json());
  return { product }
}

Now place <product-details> into a <Suspense>'s <template #default>, providing a fallback template (which will be rendered while <Suspense> resolves all the async components found in its default template):

<Suspense>
  <template #default>
    <product-details></product-details>
  </template>
  <template #fallback>
    Product is loading...
  </template>
</Suspense>

The beauty (and elegance) of using <Suspense> is that the parent doesn't need to know the actual condition(s) for which the markup is not yet to be rendered. It simply waits for all suspensible components to resolve.
In short, using <Suspense> you no longer need to hardcode the rendering logic into the template using v-ifs and specifying the condition in clear, on the wrapper. Each async child component contains its own condition and all it announces to the parent is: i'm done. When all are done, they're rendered.

like image 144
tao Avatar answered Oct 13 '22 21:10

tao