Guys I'm facing a problem in react-slick
slider. I'm rendering a card on the slider depends upon the array length. I'm also rendering custom next
and previous
buttons which triggers the next
and previous
functions to change the slide. I also wanted to this slider to be responsive. It means it has a functionality to scroll the slides according to the defined settings.
My defined settings for the breakpoints:
992
> slidesToScroll = 3
577 - 991
slidesToScroll = 2
0 - 576
slidesToScroll = 1
The next
button will be disabled when there is no forward slide and the previous
button will be disabled when there is no back slide. To achieve this functionality, there is a function named afterChange that returns the current active index of the slides. So I assign that index on the state
.
But I think my logic for the next
button is not correct.
What are my issues?
Is there any way to refresh
the slider when the window is resized because the slides are not reset according to the breakpoints
?
The next
button is not disabled at the breakpoint (0 - 576)
even if there is no slide ahead.
When the user changes a single slide and then resizes the window
to move to the next slide, the Next
button becomes inactive.
I think the problem is in my logic that is written inside renderArrows
function.
See this:
Codesandbox: View this
Code:
import React, { useState, useRef } from "react";
import Slider from "react-slick";
// Constant Variables
// Slides scroll behavior on different sizes
const TOTAL_SLIDES = 6;
const DESKTOP_SLIDES_SCROLL = 3;
const TABLET_SLIDES_SCROLL = 2;
const MOBILE_SLIDES_SCROLL = 1;
/**
* It will return the JSX and register the callbacks for next and previous slide.
* @param prevCallback {function} - Go back to the previous slide
* @param nextCallback {function} - Go to the next slide
* @param state {object} - Holds the state of your slider indexes
* @param totalSlides {number} - Holds the total number of slides
* @return {*} - Returns the JSX
*/
const renderArrows = (
prevCallback,
nextCallback,
{ currentIndex, slidesToScroll },
totalSlides
) => {
const cycleDots =
currentIndex === 0 ? 1 : Math.ceil(totalSlides / slidesToScroll);
return (
<div>
<button disabled={currentIndex === 0} onClick={prevCallback}>
Previous
</button>
<button disabled={currentIndex > cycleDots} onClick={nextCallback}>
Next
</button>
</div>
);
};
const App = () => {
// We just have to keep track of the index by keeping it in the state
const [state, setState] = useState({ currentIndex: 0, slidesToScroll: 0 });
// To access underlying DOM object for the slider
const sliderRef = useRef();
// Trigger next method to show the next slides
const next = () => {
sliderRef.current.slickNext();
};
// Trigger previous method to show the previous slides
const previous = () => {
sliderRef.current.slickPrev();
};
// Slider Settings
const settings = {
slidesToShow: 3,
dots: false,
draggable: false,
slidesToScroll: DESKTOP_SLIDES_SCROLL,
arrows: false,
speed: 1300,
autoplay: false,
centerMode: false,
infinite: false,
afterChange: indexOfCurrentSlide => {
setState({
currentIndex: indexOfCurrentSlide,
slidesToScroll: DESKTOP_SLIDES_SCROLL
});
},
responsive: [
{
breakpoint: 992,
settings: {
slidesToShow: 2,
slidesToScroll: TABLET_SLIDES_SCROLL,
afterChange: indexOfCurrentSlide => {
setState({
currentIndex: indexOfCurrentSlide,
slidesToScroll: TABLET_SLIDES_SCROLL
});
}
}
},
{
breakpoint: 576,
settings: {
slidesToShow: 1,
slidesToScroll: MOBILE_SLIDES_SCROLL,
afterChange: indexOfCurrentSlide => {
setState({
currentIndex: indexOfCurrentSlide,
slidesToScroll: MOBILE_SLIDES_SCROLL
});
}
}
}
]
};
return (
<div className="app">
<div>
<h1>Slider Buttons</h1>
{renderArrows(previous, next, state, TOTAL_SLIDES - 1)}
</div>
{/* Slider */}
<Slider {...settings} ref={slider => (sliderRef.current = slider)}>
{[...Array(TOTAL_SLIDES)].map((_, index) => {
return (
<div className="card" key={index}>
<div className="card-img-top">
<svg
className="svg"
width="100%"
height="225"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid slice"
focusable="false"
>
<rect width="100%" height="100%" fill="#55595c" />
<text x="50%" y="50%" fill="#eceeef" dy=".3em">
Image {index + 1}
</text>
</svg>
</div>
<div className="card-body">
<p className="card-text">
This is a wider card with supporting text below.
</p>
</div>
</div>
);
})}
</Slider>
</div>
);
};
export default App;
Looks like everything is working already but the disabled Next
button. I think it should work already by changing the disabled
condition to check the amount of slides the slider is at.
Changing the renderArrow
body to this in the provided code sandbox seems like it works:
const lastElementsIndex = totalSlides - slidesToScroll;
return (
<div>
<button disabled={currentIndex === 0} onClick={prevCallback}>
Previous
</button>
<button disabled={currentIndex > lastElementsIndex} onClick={nextCallback}>
Next
</button>
</div>
);
Since the initial state is not synchronized with the actual values depending on the breakpoint, there might be a special case as pointed out in the comments. Using onReInit
as a non-arrow function should get access to the this
object of the Slick slider itself. From there, the correct slidesToScroll
can be retrieved:
const settings = {
// ... other settings ...
onReInit: function() {
const slidesToScroll = this.slidesToScroll;
if (slidesToScroll !== state.slidesToScroll) {
setState(state => ({ ...state, slidesToScroll }));
}
},
// ... other settings ...
};
In a previous edit, I've proposed using onInit
- using onReInit
has the benefit of shooting on window resize as well. Thus it should also synchronize the state correctly when resizing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With