I'm using React Slick centerMode
as my image carousel and trying to show 3 images at a time. Also, I would like to show the title of the image when it is centered (current index) only. The problem is it will show all the title at the same time. How can I hide left and right images' title and only shows the middle image's title?
my code:
function ImageSlider({ imageList }) {
const [currentSlide, setCurrentSlide] = useState(0);
const [indexSlide, setIndexSlide] = useState(0);
function ShowTitle(title) {
// If the index is on focused
if (currentSlide === indexSlide) {
return <p>{title}</p>;
}
// Otherwise return empty
return <p></p>;
}
function handleAfterChange(index) {
setIndexSlide(index);
const centerSlide = document.querySelector(
"div.slick-slide.slick-active.slick-center.slick-current"
);
setCurrentSlide(parseInt(centerSlide.getAttribute("data-index")));
}
return (
<Slider
className="center"
centerMode={true}
infinite={true}
autoplay={true}
autoplaySpeed={2000}
slidesToShow={3}
slidesToScroll={1}
afterChange={handleAfterChange}
>
{imageList.map((image) => (
<div key={image.id} id={image.id}>
<img
alt="image"
width={270}
height={120}
src={image.url}
/>
// I want this to show when it's centered, and hidden when it's not centred
<ShowTitle title={image.title} key={image.id} />
</div>
))}
</Slider>
);
}
The carousel html when index number 2 (out of 5) is centered:
<div class="slick-list" style="padding:0px 60px">
<div class="slick-track" style="width: 3640px; opacity: 1; transform: translate3d(-1040px, 0px, 0px); transition: -webkit-transform 500ms ease 0s;">
<div data-index="-4" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
<div data-index="-3" tabindex="-1" class="slick-slide slick-center slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
<div data-index="-2" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
<div data-index="-1" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
<div data-index="0" class="slick-slide" tabindex="-1" aria-hidden="true" style="outline: none; width: 260px;">...</div>
<div data-index="1" class="slick-slide slick-active" tabindex="-1" aria-hidden="true" style="outline: none; width: 260px;">...</div>
<div data-index="2" class="slick-slide slick-active slick-center slick-current" tabindex="-1" aria-hidden="true" style="outline: none; width: 260px;">...</div>
<div data-index="3" class="slick-slide slick-active" tabindex="-1" aria-hidden="true" style="outline: none; width: 260px;">...</div>
<div data-index="4" class="slick-slide" tabindex="-1" aria-hidden="true" style="outline: none; width: 260px;">...</div>
<div data-index="5" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
<div data-index="6" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
<div data-index="7" tabindex="-1" class="slick-slide slick-center slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
<div data-index="8" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
<div data-index="9" tabindex="-1" class="slick-slide slick-cloned" aria-hidden="true" style="width: 260px;">...</div>
</div>
</div>
Your problem stems from how you're trying to do your logic against state variables. There's no connection between your state and your ShowTitle
component, other than referring to them. Whenever you page through the carousel, you're changing both state variables so they will always be the same.
Unfortunately, the library you've chosen for your carousel forces you to rely on an index, so I would suggest you use the index
argument on the map function to assign indexes to your ShowTitle
components.
Note: React will warn you if you try to use indexes for keys, as that has the potential for strange behavior. Keep using the id for that.
function ImageSlider({ imageList }) {
const [currentSlide, setCurrentSlide] = useState(0);
// Since this is a component, you need to destructure props
function ShowTitle({ title, id }) {
// If the index is on focused
if (id === currentSlide) {
return <p>{title}</p>;
}
// Otherwise return empty
return <p></p>;
}
// I'm not sure what props you're going to get here; it'll be whatever
// the Slider provides you with when afterChange is fired
function handleAfterChange(index) {
setCurrentSlide(index);
}
return (
<Slider
className="center"
centerMode={true}
infinite={true}
autoplay={true}
autoplaySpeed={2000}
slidesToShow={3}
slidesToScroll={1}
afterChange={handleAfterChange}
>
{/* Add index argument to map here (this is provided automatically) */}
{imageList.map((image, index) => (
<div key={image.id} id={image.id}>
<img
alt="image"
width={270}
height={120}
src={image.url}
/>
{/* Add index prop to ShowTitle */}
<ShowTitle title={image.title} index={index} />
</div>
))}
</Slider>
);
}
If you want to optimize this you can completely do away with the ShowTitle
component and conditionally render your p
tag.
...
{index === currentSlide ? <p>{image.title}</p> : <p></p>}
...
When react renders all of your code, it is going to call the ShowTitle
component, because it is, in fact, a component with the way you've written this. A function component is just a function that takes in props as its argument, and returns some JSX. Since it will call this for every loop over map, it will compare the currently set state (which doesn't change until the user does something to invoke afterChange
) so it will compare the same state variables every time, hence why all of the titles show. When you do invoke afterChange
, it always sets the two state variables to the same thing and changing state causes a rerender. This is why all of the titles are always present.
To rectify this, you need to have the rendering of a particular slide tied to the currently selected index in the carousel, and you need your component to know where it is in the carousel (by way of the index in the map function) to be able to compare to the currently selected index.