Gert-Jan Kooijmans
Gert-Jan Kooijmans

Reputation: 472

Initialise third party library using React hooks

I want to initialise a Swiper instance when a certain component mounts using React hooks. On resize I want to destroy or update the Swiper instance depending on the viewport width.

I'm initialising the plugin with the useEffect hook where I'm calling a function stored as a const.

const MyComponent = () => {
  const [swiper, setSwiper] = useSate(null);
  const element = useRef(null);

  const initSwiper = useCallback(() => {
    // swiper is always null here so I can't destroy the instance
    if (MediaQuery.is('large up') && swiper !== null) {
      return setSwiper(swiper.destroy());
    }

    if (MediaQuery.is('small up')) {
      const currentSwiper = new Swiper(element.current, swiperSettings);
      currentSwiper.init();

      return setSwiper(currentSwiper);
    }
  }, [swiper, swiperSettings]);

  useEffect(() => {
    initSwiper();
    window.addEventListener('resize', initSwiper);

    return () => {
      window.removeEventListener('resize', initSwiper);
    }
  }, []);

  return (
    <div ref={element}>...</div>
}

I would like to know how I can access the Swiper instance after initialisation. Should I use a ref? I'm not really sure what the best way is to handle this.

Upvotes: 1

Views: 1192

Answers (1)

Ori Drori
Ori Drori

Reputation: 192242

You don't actually need state here. Since the useEffect() callback is a closure, and you don't use swiper outside of the closure, create a variable (let swiper), and assign the current instance of Swiper to the variable. You should also declare initSwiper inside the closure, and you don't need (and actually can't) to wrap it with useEffect() since the useEffect() block only runs on init.

Note: swiperSettings are not coming from the props or state, so the useEffect() block is not dependant on them. If you need to change it via props, pass them via a ref.

const MyComponent = () => {
  const element = useRef(null);

  useEffect(() => {
    let swiper = null;

    const initSwiper = () => {
      if (MediaQuery.is('large up') && swiper !== null) {
        swiper = swiper.destroy();
      } else if (MediaQuery.is('small up')) {
        // swiper.destroy(); // should probably start by destroying the old swiper
        swiper = new Swiper(element.current, swiperSettings);
        swiper.init();
      }
    };

    window.addEventListener('resize', initSwiper);

    return () => {
      window.removeEventListener('resize', initSwiper);
    }
  }, []);

  return (
    <div ref={element}>...</div>
  );
};

And used as a custom hook (as suggest by @PatrickRoberts):

const useSwiper = () => {
  const element = useRef(null);

  useEffect(() => {
    let swiper = null;

    const initSwiper = () => {
      if (MediaQuery.is('large up') && swiper !== null) {
        swiper = swiper.destroy();
      } else if (MediaQuery.is('small up')) {
        // swiper.destroy(); // should probably start by destroying the old swiper
        swiper = new Swiper(element.current, swiperSettings);
        swiper.init();
      }
    };

    window.addEventListener('resize', initSwiper);

    return () => {
      window.removeEventListener('resize', initSwiper);
    }
  }, []);

  return element;
};

const MyComponent = () => (
  <div ref={useSwiper()} />
);

Upvotes: 2

Related Questions