React-id-swiper: Cannot use this.setState in onSlideChangeEnd

Created on 22 Jan 2017  路  2Comments  路  Source: kidjp85/react-id-swiper

I would like to call this.setState from the onSlideChangeEnd in order to keep track of the activeIndex in the state of the container component (that has the Swiper component). However, this triggers an error: swiper.js:4147 Uncaught TypeError: Cannot read property 'emitterEventListeners' of null whenever the slide is changed.

Interestingly, the activeIndex in the<p>tag does get updated correctly when clicking between different slides using the pagination buttons, but the slides themselves keep snapping back to the initial slide.

I don't understand why you cannot call this.setState within Swiper's transition callbacks. Is there another way for the state to be updated with Swiper's activeIndex?

class Foo extends React.Component {

  constructor(props) {
    super(props)
    this.state = {activeIndex: null}
    this.swiper = null
  }

  componentDidMount(){
    this.setState({activeIndex: this.swiper.activeIndex})
  }

  render() {

    const params = {
      pagination: '.swiper-pagination',
      paginationClickable: true,
      onSlideChangeEnd:(swiper)=>{
        this.setState({activeIndex: swiper.activeIndex})
      },
      onInit: (swiper)=>{this.swiper = swiper},
      runCallbacksOnInit: true,
    }

    return (
      <div>
        <p>Current Active Index: {this.state.activeIndex}</p>
        <Swiper {...params}>
           <div><Bar /></div>
           <div><Baz /></div>
        </Swiper>
      <div>
    );
  }
}

Most helpful comment

Hi @evanzio,
not sure if it's still relevant for you, but I stumbled over the same problem of holding the activeIndex in the normal react state. My attempt looks like this:

import React from 'react';
import Swiper from 'react-id-swiper';

class Foo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      swiper: null,
      activeSlideIndex: null,
    };

    this.swiperRef = this.swiperRef.bind(this);
  }

  swiperRef(ref) {
    this.setState({ swiper: ref.swiper });
  }

  render() {
    const swiperParams = {
      slidesPerView: 'auto',
      centeredSlides: true,
      slideToClickedSlide: true,
      on: {
        slideChangeTransitionStart:
          () => this.state.swiper && this.setState({ activeSlideIndex: this.state.swiper.activeIndex }),
      },
    };

    return (
      <div>
        <span>{this.state.activeSlideIndex}</span>
        <Swiper {...swiperParams} ref={this.swiperRef}>
          {this.props.slides.map(slide => (
            <div>{slide.value}</div>
          ))}
        </Swiper>
      </div>
    );
  }
};

All 2 comments

Hi @evanzio , thank you for your report. Currently, it's impossible to setState in swiper callback function, because every time new state change it will re-render the Swiper component . That why u receive the error Uncaught TypeError: Cannot read property 'emitterEventListeners' of null. Instead of putting in the same component, I recommend you to separate the swiper and <p>Current Active Index: {this.state.activeIndex}</p> in two component. If you use some state library management like Redux or Mobx, you can put the action method to update state inside the swiper callback function. Thanks :)

Hi @evanzio,
not sure if it's still relevant for you, but I stumbled over the same problem of holding the activeIndex in the normal react state. My attempt looks like this:

import React from 'react';
import Swiper from 'react-id-swiper';

class Foo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      swiper: null,
      activeSlideIndex: null,
    };

    this.swiperRef = this.swiperRef.bind(this);
  }

  swiperRef(ref) {
    this.setState({ swiper: ref.swiper });
  }

  render() {
    const swiperParams = {
      slidesPerView: 'auto',
      centeredSlides: true,
      slideToClickedSlide: true,
      on: {
        slideChangeTransitionStart:
          () => this.state.swiper && this.setState({ activeSlideIndex: this.state.swiper.activeIndex }),
      },
    };

    return (
      <div>
        <span>{this.state.activeSlideIndex}</span>
        <Swiper {...swiperParams} ref={this.swiperRef}>
          {this.props.slides.map(slide => (
            <div>{slide.value}</div>
          ))}
        </Swiper>
      </div>
    );
  }
};
Was this page helpful?
0 / 5 - 0 ratings