I started implementing a carousel in react, furthermore I wanted to give it an infinite scroll effect, that is if you keep clicking right and reaches the end. It starts from the beginning.
The funny thing is, I had to use useState and I am noticing very strange behavior. It never reaches the last div but goes to the first one instantly (No scrolling effect) and scrolls to the second one.
This the main file where the scrolling is happening.
Carousel.tsx
import React, { FC, useEffect, useLayoutEffect, useRef, useState } from "react";
import { useStyles, useStylesClasses } from "./CarouselCSS";
import ArrowRightAltIcon from '@mui/icons-material/ArrowRightAlt';
interface Props {
className?: string;
}
type Scroll = "left" | "right";
type Index = {
sign: "To" | "By";
value: number;
}
export const Carousel: FC<Props> = ({className, children}) => {
const {Root, Carousel} = useStyles();
const classes = useStylesClasses();
const ref = useRef(null);
const firstRun = useRef(true);
const [activeIndex, setActiveIndex] = useState<Index>({
sign: "To",
value: 0
});
// Triggers before rendering the page
// useLayoutEffect(()=>{
// console.log(ref.current.children.length);
// }, [ref])
useEffect(() => {
if(!firstRun.current)
if (activeIndex.sign === "To"){
console.log({"To": activeIndex.value})
ref.current.scrollTo({
left: (activeIndex.value*ref.current.clientWidth),
behavior: "smooth",
})
}
else{
console.log("By")
ref.current.scrollBy({
left: ref.current.clientWidth,
behavior: "smooth",
})
}
else
firstRun.current = false;
}, [activeIndex])
// controlling scrolling and making it infinite
const scroll = (value: Scroll) => {
switch(value){
case "left": {
console.log("Left")
if(activeIndex.value === 0){
setActiveIndex({sign: "By", value: ref.current.children.length - 1});
}
else{
setActiveIndex({sign: "To", value: activeIndex.value - 1});
}
}
break;
case "right": {
console.log("Right")
if(activeIndex.value === ref.current.children.length - 1){
setActiveIndex({sign: "To", value: 0});
}
else{
setActiveIndex({sign: "By", value: activeIndex.value + 1});
}
}
break;
}
}
return (
<Root>
<div onClick={() => {
scroll("left");
}
}
>
<ArrowRightAltIcon style={{transform: 'rotate(180deg)'}} className={classes.arrow}/>
</div>
<Carousel className={className} ref={ref}>
{children}
</Carousel>
<div onClick={() => {
scroll("right");
}}
>
<ArrowRightAltIcon className={classes.arrow}/>
</div>
</Root>
)
}
If this doesn't make proper sense, I am also attaching the code sandbox link to the whole thing.
Update
As suggested, the component re-renders and hence why the instant appearance of the 1st div. Now, what I am trying to understand here is, why it re-renders in certain cases and does scrolling in some? Since, I am using useState, shouldn't it re-render it every time?
https://codesandbox.io/embed/keen-wright-9n94s?fontsize=14&hidenavigation=1&theme=dark
Any help is appreciated!