Search code examples
javascriptreactjscarouselreact-slick

Is there a way with React Slick to have a Carousel inside a Carousel?


Is there a way with React Slick to have Carousel inside Carousel ?

import Slider from "react-slick";

<Slider
    {...settings}
>
        <div/>
        <div>
            <Slider
                {...settings}
            >
                ...
            </Slider>
       </div>
       <div/>
</Slider>

I tried this kind of code but it completely messed up both carousels.

I don't need to have swipe, dots or arrow, carousel are fully controlled using slickGoTo.


Solution

  • I solved my issue, we can have 2 levels multiple nested Sliders with this component :

    Carousel.js

    import React, {useEffect, useRef, useState} from 'react';
    import PropTypes from 'prop-types';
    import Slider from 'react-slick';
    
    import 'slick-carousel/slick/slick.css';
    import './carousel.scss';
    
    function isInt(value) {
        return !isNaN(value) && (function (x) {
            return (x | 0) === x;
        })(parseFloat(value))
    }
    
    function resize() {
        // Trigger resize (IE compatible)
        if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) {
            let event = document.createEvent('UIEvents');
            event.initUIEvent('resize', true, false, window, 0);
            window.dispatchEvent(event);
        } else {
            window.dispatchEvent(new Event('resize'));
        }
    }
    
    function Carousel(props) {
        const {children, className, initialSlide, onChange, current, speed, ...rest} = props;
    
        const sliderRef = useRef(null);
    
        useEffect(() => {
            initialSlide && setStep(initialSlide)
        }, [initialSlide]);
    
        useEffect(() => {
            isInt(current) && setStep(current);
        }, [current]);
    
        const [step, setStep] = useState(initialSlide || 0);
        useEffect(() => {
            step < 0 && setStep(0);
            step > children.length && setStep(children.length);
    
            sliderRef.current.slickGoTo(step);
            onChange && onChange(step);
        }, [step]);
    
        const settings = {
            accessibility: false,
            adaptiveHeight: false,
            arrows: false,
            className: className,
            dots: false,
            infinite: false,
            initialSlide: initialSlide || 0,
            slideIndex: initialSlide || 0,
            slidesToScroll: 1,
            slidesToShow: 1,
            speed: speed || 500,
            swipe: false
        };
    
        const handleBeforeChange = (_, index) => {
            onChange && onChange(index);
    
            // This setTimeout is needed for adaptive height in nested Carousel
            setTimeout(() => {
                resize();
            }, speed || 500);
        };
    
        return (
            <Slider
                beforeChange={handleBeforeChange}
                ref={sliderRef}
                {...settings}
                {...rest}
            >
                {React.Children.map(children, (child, index) => (
                        child
                            ? <React.Fragment
                                key={`slide-${index}`}
                            >
                                {React.cloneElement(child, {
                                    step: step,
                                    setStep: setStep
                                })}
                            </React.Fragment>
                            : () => {
                            }
                    )
                )}
            </Slider>
        );
    }
    
    Carousel.propTypes = {
        className: PropTypes.string,
        current: PropTypes.number,
        initialSlide: PropTypes.number,
        speed: PropTypes.number,
        onChange: PropTypes.func
    };
    
    export default Carousel;
    
    

    Resize for adaptiveHeight is handled at the beginning and the end of animation.

    Use it like this :

    <Carousel adaptiveHeight speed={1000} {...props}>
       <Component/>
       <Carousel adaptiveHeight speed={750} {...props}>
           <Component/>
           <Component/>
           <Component/>
           <Component/>
       </Carousel/>
       <Component/>
    </Carousel>
    

    /!\ You cannot have autoFocus prop on <input> inside your Slider (except for step 1)