This is a relatively simple problem, but I haven't been able to solve it. I have built the following carousel / slider:
const BlogPostCardSlider = ({ children }) => {
const [activeSlide, setActiveSlide] = useState(0);
const activeSlideRef = useRef(null);
const wrapperRef = useRef(null);
const firstRenderRef = useRef(true);
useEffect(() => {
if (firstRenderRef.current) {
//this is checking whether its the first render of the component. If it is, we dont want useEffect to run.
firstRenderRef.current = false;
} else if (activeSlideRef.current) {
activeSlideRef.current.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'nearest'
});
}
}, [activeSlide]);
const moveRight = () => {
if (activeSlide + 1 >= children.length) {
return children.length - 1;
}
return activeSlide + 1;
};
const moveLeft = () => {
if (activeSlide - 1 <= 0) {
return 0;
}
return activeSlide - 1;
};
return (
<div id="trap" tabIndex="0">
<button onClick={() => setActiveSlide(moveLeft)}>PREV</button>
{children.map((child, i) => {
return (
<SlideLink
key={`slideLink-${i}`}
isActive={activeSlide === i}
onClick={e => {
setActiveSlide(i);
}}
>
{i + 1}
</SlideLink>
);
})}
<Wrapper
onScroll={e => {
let { width } = wrapperRef.current.getBoundingClientRect();
let { scrollLeft } = wrapperRef.current;
if ((scrollLeft / width) % 1 === 0) {
setActiveSlide(scrollLeft / width);
}
}}
ref={wrapperRef}
>
{children.map((child, i) => {
return (
<Slide
key={`slide-${i}`}
ref={i === activeSlide ? activeSlideRef : null}
>
{child}
</Slide>
);
})}
</Wrapper>
<button onClick={() => setActiveSlide(moveRight)}>NEXT</button>
</div>
);
};
export default BlogPostCardSlider;
It displays its children as Slides in a carousel. You can navigate the carousel by pressing the 'NEXT' or 'PREVIOUS' buttons. There is also a component that shows you what slide you're on (e.g.: 1 2 *3* 4 etc.). I call these the SlideLink(s). Whenever the activeSlide updates, the SlideLink will update and highlight you the new current Slide.
Lastly, you can also navigate by scrolling. And here's the problem:
Whenever somebody scrolls, I am checking what slide they're on by doing some calculations:
onScroll={e => {
let { width } = wrapperRef.current.getBoundingClientRect();
let { scrollLeft } = wrapperRef.current;
if ((scrollLeft / width) % 1 === 0) {
setActiveSlide(scrollLeft / width);
}
}}
...the result of this is that setActiveSlide is only called once the slide completely in view. This results in a laggy experience : the user has scrolled to the next slide, the next slide is in view, but the calculation is not completed yet (since it is only completed once the Slide is 100% in view), so the active SlideLink gets updated very late in the process.
How would I solve this? Is there some way to optimistically update this?
from Simple Carousel in React - How to register Scrolling to next Slide?
No comments:
Post a Comment