swiper/react onSlideChange's callback wouldn't be updated

Created on 11 Aug 2020  路  6Comments  路  Source: nolimits4web/swiper

This is a (multiple allowed):

  • [x] bug
  • [ ] enhancement
  • [ ] feature-discussion (RFC)

What you did

A callback in my react component is set to swiper/react component, and I want to update that function

Expected Behavior

callback function would be updated

Actual Behavior

please refer to the provided codesandbox link, reproducing steps:

  • click the random button, the list is updated
  • change active slide with navigation, the List in onSlideChange Cb wouldn't be updated because list in the callback stays the same

I am not sure if this is related to how event listeners are updated to the swiper instance from react component. I quickly checked the source code but I can't find any clue. I think it might be much easier to look for help here so if someone can give me some hint that would be really appreciated!

React

Most helpful comment

useRef as another workaround to get latest state value in the component when onSlideChange not get updated.

import React, { useState, useCallback, useRef } from "react";
import SwiperCore, { Navigation } from "swiper";
import { Swiper, SwiperSlide } from "swiper/react";
import "./styles.css";

SwiperCore.use([Navigation]);

export default function App() {
  const listRef = useRef(null);
  const [list, setList] = useState([
    {
      id: "1"
    },
    {
      id: "2"
    }
  ]);
  const setListAndRef = (l) => {
    setList(l);
    listRef.current = l;
  };
  const [listInCb, setListInCb] = useState(list);

  const onSlideChange = useCallback(() => {
    // list stays the same as the intial value
    console.log(JSON.stringify(listRef.current));
    setListInCb(listRef.current);
  }, []);

  const onRandomClick = useCallback(() => {
    setListAndRef([
      {
        id: Math.floor(1000 * Math.random())
      },
      {
        id: Math.floor(1000 * Math.random())
      }
    ]);
  }, []);

  const items = list.map((li) => (
    <SwiperSlide key={li.id}>{li.id}</SwiperSlide>
  ));
  return (
    <div className="App">
      <Swiper navigation onSlideChange={onSlideChange}>
        {items}
      </Swiper>
      <button type="button" onClick={onRandomClick}>
        Random list
      </button>
      <p>List in onSlideChange Cb</p>
      {JSON.stringify(listInCb)}
    </div>
  );
}

based on your example

https://codesandbox.io/s/swiper-event-listener-forked-pe3ex?file=/src/App.js:0-1295

All 6 comments

I workaround by adding event listeners to swiper instance in useEffect hook, ie.

const Component = () => {
  const [swiperInstance, setSwiperInstance] = useState(null);

  const onSlideChange = useCallback(() => {
    setListInCb(list);
  }, [list]);

  useEffect(() => {
    if (swiperInstance) {
      swiperInstance.on("slideChange", onSlideChange);
    }
  }, [swiperInstance, onSlideChange]);

  return (
    <Swiper navigation>
      {items}
    </Swiper>
  );
}

File: App_fixed.jsx

const onRandomClick = useCallback(() => {
    setList([
      {
        id: Math.floor(10 * Math.random())
      },
      {
        id: Math.floor(10 * Math.random())
      },
      {
        id: Math.floor(10 * Math.random())
      }
    ]);
  }, []);

I added new item, but swper items are not update after clicked.

May need to call swiper.updateSlides()

useRef as another workaround to get latest state value in the component when onSlideChange not get updated.

import React, { useState, useCallback, useRef } from "react";
import SwiperCore, { Navigation } from "swiper";
import { Swiper, SwiperSlide } from "swiper/react";
import "./styles.css";

SwiperCore.use([Navigation]);

export default function App() {
  const listRef = useRef(null);
  const [list, setList] = useState([
    {
      id: "1"
    },
    {
      id: "2"
    }
  ]);
  const setListAndRef = (l) => {
    setList(l);
    listRef.current = l;
  };
  const [listInCb, setListInCb] = useState(list);

  const onSlideChange = useCallback(() => {
    // list stays the same as the intial value
    console.log(JSON.stringify(listRef.current));
    setListInCb(listRef.current);
  }, []);

  const onRandomClick = useCallback(() => {
    setListAndRef([
      {
        id: Math.floor(1000 * Math.random())
      },
      {
        id: Math.floor(1000 * Math.random())
      }
    ]);
  }, []);

  const items = list.map((li) => (
    <SwiperSlide key={li.id}>{li.id}</SwiperSlide>
  ));
  return (
    <div className="App">
      <Swiper navigation onSlideChange={onSlideChange}>
        {items}
      </Swiper>
      <button type="button" onClick={onRandomClick}>
        Random list
      </button>
      <p>List in onSlideChange Cb</p>
      {JSON.stringify(listInCb)}
    </div>
  );
}

based on your example

https://codesandbox.io/s/swiper-event-listener-forked-pe3ex?file=/src/App.js:0-1295

also same with onClick callback
can it be fixed?

From what I can gather, https://github.com/nolimits4web/swiper/blob/f8ca5c094b7fa5d361b80510340256ecbfca75f9/src/react/get-changed-params.js should properly notice that the event handler changed.

Then https://github.com/nolimits4web/swiper/blob/f8ca5c094b7fa5d361b80510340256ecbfca75f9/src/react/update-swiper.js doesn't do anything with that info.

It would need to .off and .on the changed event handlers.

Was this page helpful?
0 / 5 - 0 ratings