vue-slick-carousel icon indicating copy to clipboard operation
vue-slick-carousel copied to clipboard

Mismatching childNodes vs. VNodes with responsive options (nuxtjs)

Open Brewal opened this issue 5 years ago • 24 comments

When using the responsive option and matching a breakpoint, after a page reload, an error is thrown :

[Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. Bailing hydration and performing full client-side render.

I'm using this configuration :

{
  speed: 500,
  arrows: false,
  dots: true,
  slidesToShow: 3,
  slidesToScroll: 3,
  responsive: [
    {
      breakpoint: 576,
      settings: {
        slidesToShow: 1,
        slidesToScroll: 1
      }
    }
  ]
}

Brewal avatar Mar 26 '20 16:03 Brewal

yes. I get this promlem too. Im waiting solution problem

PHPDevFrozenRain avatar Mar 31 '20 23:03 PHPDevFrozenRain

I've tested using https://github.com/kyuwoo-choi/nuxt-vue-slick-carousel-example with responsive settings. And I can't reproduce this issue. @Brewal @PHPDevFrozenRain could you provide a demo I can reproduce?

Besides of that, I see nuxt server renders not considering responsive setting as nuxt can not know the client screen size. It's a limitation of this carousel.

kyuwoo-choi avatar Apr 01 '20 07:04 kyuwoo-choi

the bug happens 2 div and more. Here is an example (uncomment responsive) <vue-slick-carousel v-bind="settings"> <div>1</div> <div>2</div> </vue-slick-carousel>

            settings:{
                slidesToShow: 3,
                arrows: true,
//                responsive: [
//                    {
//                        breakpoint: 2500,
//                        settings: {
//                            slidesToShow: 3,
//                            slidesToScroll: 1,
//                            infinite: true,
//                            dots: true,
//                            arrows: true,
//                        }
//                    },
//                    {
//                        breakpoint: 991,
//                        settings: {
//                            slidesToShow: 2,
//                            slidesToScroll: 1,
//                            dots: true,
//                            arrows: true,
//                        }
//                    },
//                    {
//                        breakpoint: 767,
//                        settings: {
//                            slidesToShow: 1,
//                            slidesToScroll: 1,
//                            dots: true
//                        }
//                    },
//                ]
            },

PHPDevFrozenRain avatar Apr 02 '20 19:04 PHPDevFrozenRain

Besides of that, I see nuxt server renders not considering responsive setting as nuxt can not know the client screen size. It's a limitation of this carousel.

yes. but it continious reproducing the bug and doesn't let use ssr. I noticed it on version 1.0.5 but I'm not sure about 1.0.3 version. In your example you use 1.0.2 version.

PHPDevFrozenRain avatar Apr 02 '20 19:04 PHPDevFrozenRain

I have this issue with 1.0.2 (and 1.0.5 also). To reproduce, you need to have "universal" mode in Nuxt.js and refresh the page on some "breakpoint" from responsive setting. The error (warning) will show in console.

julpat avatar Apr 05 '20 18:04 julpat

I temporarily solved this by wrapping the carousel with responsive configuration in <client-only>...</client-only>

subjacked avatar Apr 07 '20 10:04 subjacked

Same problem. Tell me how to fix it?

cherneckiy avatar Apr 08 '20 20:04 cherneckiy

I still have no idea how to support responsive SSR. Yet, I reproduced this error and need to think of ways how to deal with it. I'd love to listen to the opinions.

kyuwoo-choi avatar Apr 09 '20 00:04 kyuwoo-choi

Sadly I dont have so much time to consider all consequences of your code, but from quick check, I would start to avoid using created method for DOM manipulating. In InnerSlider.vue::72 you got ssrInit() which is making preClones and postClones. It is not importent to have clones in SSR version, also you dont know the window sizes, so can not decide corectly based on "responsive". Also in VueSlickCarousel.vue::105 is weird to call this.makeBreakpoints() in created methods. I guess it should be in mounted hook?

julpat avatar Apr 09 '20 06:04 julpat

I understand the responsiveness of the carousel can cause some issue when used in conjunction of SSR. As @julpat said, you should have some options to prevent this issue (you should indeed be using the mounted event to render and patch the virtual DOM) but maybe it will take more efforts to make it work.

I didn't have the time either to look at the source code but I hope I will.

For now, maybe I will conditionally render multiple carousel based on the user agent using a library such as mobile-device-detect or just ignore the error !

Brewal avatar Apr 09 '20 09:04 Brewal

are there going to be fixes? many people use carousel because of ssr.

PHPDevFrozenRain avatar May 01 '20 21:05 PHPDevFrozenRain

Agree, maybe the Hotfix is removing "supports true SSR" from README.md 🤔

julpat avatar May 02 '20 07:05 julpat

sure It should be fixed. I just have to find my time on it.

kyuwoo-choi avatar May 08 '20 08:05 kyuwoo-choi

Same problem!!!

escael avatar Jun 01 '20 16:06 escael

same here, will it be fixed?

jaytrepka avatar Jun 02 '20 08:06 jaytrepka

I've found a workaround which works for SSR and does not add an additional performance burden for mobile devices.

It defines an initial mobile friendly this.settings.slidesToShow of 2 and an empty this.settings.responsive: [] . The required/desired responsive settings are applied once on VueSlickCarousel init.

Note: this.areResonsiveSettingsApplied === false check is used in order to prevent that this.responsiveSettings are applied multiple times through an init loop.

<template>
  <div class="h-screen">
    <VueSlickCarousel v-bind="settings" @init="initHandler">
      <div
        v-for="(value, index) in items"
        :key="index"
        class="h-40"
        :class="[`bg-pink-${value}`]"
      >
        {{ value }}
      </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: 'MyComponent',
  components: { VueSlickCarousel },
  data() {
    return {
      items: [100, 200, 300, 400, 500, 600, 700, 800, 900],
      settings: {
        dots: false,
        infinite: true,
        autoplay: true,
        autoplaySpeed: 3000,
        speed: 1000,
        slidesToShow: 2,
        slidesToScroll: 1,
        initialSlide: 0,
        cssEase: 'ease-in-out',
        responsive: [],
      },
      responsiveSettings: [
        {
          breakpoint: 1201,
          settings: {
            slidesToShow: 4,
          },
        },
        {
          breakpoint: 993,
          settings: {
            slidesToShow: 3,
          },
        },
        {
          breakpoint: 769,
          settings: {
            slidesToShow: 2,
          },
        },
      ],
      areResonsiveSettingsApplied: false,
    }
  },
  methods: {
    initHandler() {
      console.log('> Handler : init')
      if (this.areResonsiveSettingsApplied === false) {
        this.applyResponsiveSettings()
      }
    },
    applyResponsiveSettings() {
      console.log('> Method : applyResponsiveSettings')
      this.settings.responsive = this.responsiveSettings
      this.settings.slidesToShow = 5
      this.areResonsiveSettingsApplied = true
    },
  },
}
</script>
<style>
.slick-slider {
  & .slick-arrow.slick-arrow {
    @apply bg-red-600;
  }
}
</style>

dweiss-et avatar Jul 14 '20 14:07 dweiss-et

the solution from @dweiss-et works for me. although i'm curious about the missing @reInit event handler in your code example above.

rizkysyazuli avatar Jan 02 '21 15:01 rizkysyazuli

@rizkysyazuli you are right there is an @reInit="reinitHandler unused event handler declaration in my example. I have removed it from the example because it's not required.

dweiss-et avatar Jan 02 '21 15:01 dweiss-et

This worked for me.

<template>
  <vue-slick-carousel v-if="showSlider" v-bind="sliderSettings">
    ...
  </vue-slick-carousel>
</template>

<script>
export default {
  data () {
    return {
      showSlider: true,
      sliderSettings: {
        ... // do not include the responsive settings here
      }
    }
  },

  mounted () {
    this.showSlider = false

    this.$nextTick(() => {
      this.sliderSettings.responsive = [{
        breakpoint: 1024,
        settings: {
          ...
        }
      }]

      this.showSlider = true
    })
  }
}
</script>

gullocean avatar Feb 19 '21 15:02 gullocean

@gullocean Thanks Its working but it flickers as on initial page load, 2 are loaded and then a single is switched in mobile.

Anyway to fix this?

milindsingh avatar Mar 29 '21 06:03 milindsingh

If u are using Nuxt.js you can use device module to handle this error and don't loss SSR features on this plugin

slidesToShow: this.$device.isMobile ? 1 : 3, slidesToScroll: this.$device.isMobile ? 1 : 3,

This is not elegant at all but works as expected.

irving-caamal avatar Jul 11 '21 16:07 irving-caamal

Using the solution provided by @dweiss-e AND ensuring that the # of items in the carousel stayed consistent across both the server and the client solved this issue for me.

Originally, due to a particular cache strategy I was using, the number of items in the carousel could change depending on whether it was rendered on the client or the server.

JeffJassky avatar Nov 05 '21 21:11 JeffJassky

Having the same issue on 1.0.6. Using <client-only /> for now.

lkjimy avatar Dec 31 '21 02:12 lkjimy

I did following instead:

  computed: {
    carouselOpts () {
      switch (this.screenSize) {
        case 'sm': return { centerMode: false, slidesToShow: 1, slidesToScroll: 1 }
        case 'md': return { centerMode: false, slidesToShow: 2, slidesToScroll: 2 }
        default: return { centerMode: true, slidesToShow: 3, slidesToScroll: 3 }
      }
    },
  },
    <vue-slick-carousel
      :arrows="true"
      v-bind="carouselOpts"
      swipe-to-slide
    >

this.screenSize is set first on the backend based on 'mobile-detect' package.

vedmant avatar Oct 09 '22 16:10 vedmant