Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSR and responsive mode #1245

Open
padupuy opened this issue May 28, 2018 · 33 comments
Open

SSR and responsive mode #1245

padupuy opened this issue May 28, 2018 · 33 comments

Comments

@padupuy
Copy link

padupuy commented May 28, 2018

I already open this issue on the nextjs repo but it seems to be a problem with react-slick.

Describe the bug
When I use next.js with react-slick and responsive options, there is a bug during the rehydration phase, the src attribute of the images are mixed between the slides.

The error message is Warning: Prop src did not match. Server: "http://via.placeholder.com/350x150?text=7" Client: "http://via.placeholder.com/350x150?text=8"

To Reproduce
I made an example from examples folder : https://github.com/padupuy/next.js/tree/feature/with-react-slick

The code is right here : https://github.com/padupuy/next.js/blob/feature/with-react-slick/examples/with-react-slick/pages/index.js

Expected behavior
The image sources must not be mixed

Screenshots
capture d ecran 2018-05-16 a 18 29 07
capture d ecran 2018-05-16 a 18 31 01

System information

  • OS: macOS High Sierra 10.13.4
  • Browser (if applies) : at least chrome and safari
  • Version of Next.js: at least 5.1 and 6.0.2
@virajsinha
Copy link

I am facing a similar issue with SSR. Were you able to find a fix?

@masterGennadij
Copy link

Is any solution to prevent this bug? Thank you.

@davidsanchez96
Copy link

I don't know if is the best approach but I solved this problem by using dynamic import

import dynamic from 'next/dynamic'

const SliderComponentWithNoSSR = dynamic(import('../components/SliderComponent'), {
  ssr: false
})

export default () =>
  <div>
    <SliderComponentWithNoSSR />
  </div>

@tedlin182
Copy link

I too am having this same issue. For now I have to use another plugin since it's mixing up the content for me

@AndriiUhryn
Copy link

+1

3 similar comments
@felipetoffolo1
Copy link

+1

@josheche
Copy link

+1

@camwes
Copy link

camwes commented Nov 21, 2018

+1

@xmanzero
Copy link

xmanzero commented Dec 3, 2018

Thank you @davidsanchez96

@lucanovera
Copy link

+1

1 similar comment
@goshdarnheck
Copy link

+1

@chaance
Copy link

chaance commented Jan 31, 2019

Following @davidsanchez96's lead, I still wanted the actual content to be rendered on both the server and the client, so this was my approach.

import React, { Component } from 'react';
import dynamic from 'next/dynamic';
import classNames from 'classnames';

class Slider extends Component {
  state = {
    isServer: true
  };

  componentDidMount() {
    this.setState((state) => state.isServer && { isServer: false });
  }

  render() {
    const { settings, className, children } = this.props;
    const SliderRendered = dynamic(import('react-slick'), {
      ssr: this.state.isServer
    });

    return (
      <SliderRendered className={classNames('Slider', className)} {...settings}>
        {children}
      </SliderRendered>
    );
  }
}

export default Slider;

...or with hooks:

import React, { useState, useEffect } from 'react';
import dynamic from 'next/dynamic';
import classNames from 'classnames';

const Slider = ({ settings, className, children }) => {
  const [isServer, setServerState] = useState(true);
  const SliderRendered = dynamic(import('react-slick'), {
    ssr: isServer
  });
  useEffect(() => void setServerState(false), []);

  return (
    <SliderRendered className={classNames('Slider', className)} {...settings}>
      {children}
    </SliderRendered>
  );
};

export default Slider;

@isBatak
Copy link

isBatak commented Feb 13, 2019

This is my solution:

import React, { PureComponent } from 'react';
import SlickSlider from 'react-slick';

class Slider extends PureComponent {
  state = {
    isClient: false
  };

  componentDidMount() {
    this.setState((state) =>  { isClient: true });
  }

  render() {
    const {
        children,
        responsive,
        ...rest
    } = this.props;
    const { isClient } = this.state;

    return (
      <SlickSlider 
          key={isClient ? 'client' : 'server'}
          responsive={isClient ? responsive : null}
          {...rest}
      >
        {children}
      </SlickSlider>
    );
  }
}

export default Slider;

@romainquellec
Copy link

Well, I have this bug but I don't want a state for this.
Is @akiran is willing to fix that ? Thanks.

@rubenkuipers
Copy link

@akiran any updates regarding this issue?

@DB-Alex
Copy link

DB-Alex commented Nov 8, 2019

@akiran any updates?

@silksil
Copy link

silksil commented Nov 21, 2019

This is my solution:

import React, { PureComponent } from 'react';
import SlickSlider from 'react-slick';

class Slider extends PureComponent {
  state = {
    isClient: false
  };

  componentDidMount() {
    this.setState((state) =>  { isClient: true });
  }

  render() {
    const {
        children,
        responsive,
        ...rest
    } = this.props;
    const { isClient } = this.state;

    return (
      <SlickSlider 
          key={isClient ? 'client' : 'server'}
          responsive={isClient ? responsive : null}
          {...rest}
      >
        {children}
      </SlickSlider>
    );
  }
}

export default Slider;

This is my solution:

import React, { PureComponent } from 'react';
import SlickSlider from 'react-slick';

class Slider extends PureComponent {
  state = {
    isClient: false
  };

  componentDidMount() {
    this.setState((state) =>  { isClient: true });
  }

  render() {
    const {
        children,
        responsive,
        ...rest
    } = this.props;
    const { isClient } = this.state;

    return (
      <SlickSlider 
          key={isClient ? 'client' : 'server'}
          responsive={isClient ? responsive : null}
          {...rest}
      >
        {children}
      </SlickSlider>
    );
  }
}

export default Slider;

Thanks for this solution @isBatak !

I can't quit figure out exactly in my head what happens here; the images are still being rendered on server-side but it works because .......?

@StarpTech
Copy link

Hi, I had the same issue. The workaround works great.

@silksil I think it works because when we use different keys react won't hydrate the state from the server but creates it from scratch on the client.

Setting responsive to null on server-side prevents from creating elements that are created on client side afterward.

@akiran any hint how we can fix it?

@isBatak
Copy link

isBatak commented Nov 24, 2019

Hi @silksil, that trick is called two-pass rendering, you can find more info here https://reactjs.org/docs/react-dom.html#hydrate. In a nutshell, it will trigger second render on the client.

  1. render on server side (there is no window so media query detection won't work)
  2. hydration pass on client (window is available but if we change DOM in this phase hydration will fail)
  3. trigger second render on client by changing the key (in this phase DOM changes won't be a problem)

Overall, this problem can't be solved with JS because there is no window on server side and there is no way to detect window width. Only way to solve it is to generate pure CSS media queries and never touch the DOM structure.

@norbiu
Copy link

norbiu commented Apr 1, 2020

Any updates?

@arun-reddy-g
Copy link

@akiran any update on the issue ?

@impe93
Copy link

impe93 commented Jun 25, 2020

Inspired by the @isBatak solution, mine is using react hooks:

import { useState, useEffect } from 'react';
import SlickSlider from 'react-slick';

const Slider = ({ children, responsive, ...rest}) => {
  const [isClient, setIsClient] = useState(false);

  useEffect(() => {
    setIsClient(true);
  }, [])
  
  return (
    <SlickSlider 
        key={isClient ? 'client' : 'server'}
        responsive={isClient ? responsive : null}
        {...rest}
    >
      {children}
    </SlickSlider>
  );

}

export default Slider;

@leonace924
Copy link

import React from "react";
import Slider from "react-slick";

const settings = {
    dots: false,
    infinite: false,
    speed: 500,
    slidesToShow: 4,
    slidesToScroll: 1,
    lazyLoad: false,
    arrows: false,
    responsive: [
      {
        breakpoint: 1280,
        settings: {
          slidesToShow: 3.125,
        }
      },
      {
        breakpoint: 992,
        settings: {
          slidesToShow: 2.125,
        }
      },
      {
        breakpoint: 680,
        settings: {
          slidesToShow: 1.75,
        }
      },
  ]
}

If I use this for SSR, it gives me warning like these two

Warning: Prop `style` did not match. Server: "width:100%;left:0%" Client: "width:128%;left:0%"
Warning: Prop `className` did not match. Server: "slick-slide slick-active" Client: "slick-slide"

What is the solution for this warning?

@leonace924
Copy link

Hello, @rubenkuipers
To be honest, I need above thing urgently, I think that maybe react-slick has issue or I missed one option for SSR.

It'd be great if you advice me.
Thank you

@dpkpaa
Copy link

dpkpaa commented Oct 6, 2020

If you are using next js then just use dynamic import for that component where you used react-slick or if you are not using ssr then just use lazyload component and suspension .

@mayanksainsburys
Copy link

mayanksainsburys commented Feb 10, 2021

Tried using dynamic import const SliderRendered = dynamic(import('react-slick'));
and <SliderRendered {...settings} but still mixes the content! Please help

@dpkpaa
Copy link

dpkpaa commented Feb 11, 2021

import Slider from "react-slick"
const ExampleComponent = () => {
return (
<SliderRendered {...settings}>
//your items

);
}

use below example when you are importing your slider component to page
const ExampleComponent = dynamic(import('./ExampleComponent'));

@supriome
Copy link

supriome commented Mar 4, 2021

There have same problem when use swiper/react in nextjs with responsive mode

@anettwu
Copy link

anettwu commented May 22, 2021

// component swiper
import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperCore, { Navigation, Pagination } from 'swiper';
import 'swiper/swiper.min.css';
import 'swiper/components/pagination/pagination.min.css';
SwiperCore.use([Navigation, Pagination]);

<Swiper
navigation={{
	nextEl: '.next',
	prevEl: '.prev',
}}
breakpoints={{
	1000: {
		slidesPerView: 3,
	},
	0: {
		slidesPerView: 1,
	},
}}>
{data.map((item, index) => (
	<SwiperSlide key={index}>
		<article>
			<img src={item.thumbnailUrl} />
			<h2>{item.id}</h2>
		</article>
	</SwiperSlide>
))}
</Swiper>

//----------------------------------------------------
// index.js

const DynamicComponent = dynamic(() => import('../components/Swiper'), {
	ssr: false,
});

export default function Home({ data }) {

	return (
		<div>
			<DynamicComponent data={data} />
		</div>
	);
}

export async function getServerSideProps() {
	const res = await fetch(
		`https://jsonplaceholder.typicode.com/photos?_limit=10`
	);
	const data = await res.json();
	return { props: { data } };
}

@Commondore
Copy link

@anettwu Thanks for the hint

@AmirAmArA
Copy link

+1

@AmirAmArA
Copy link

next js 14 not working , facing hydration problems

@tulio-sandoval-aligent
Copy link

I spent a lot of time on this same issue, @leonace924. This is what I did (I'm not using Next.js!)

const [SlickSlider, setSlickSlider] = useState(null);

const loadSlider = useCallback(() => {
        const Slider = lazy(() => import('react-slick'));
        setSlickSlider(Slider);
}, []);

useEffect(() => {
        loadSlider();
}, [loadSlider]);

return (
        {SlickSlider && (<SlickSlider {...sliderSettings}>{children}</SlickSlider>)}
        {!SlickSlider && (<Fragment {...sliderSettings}>{children[0]}</Fragment>)}
)

I hope this helps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests