react-native-swiper: onIndexChanged produces Warning: Cannot update a component from inside the function body of a different component.

Which OS ?

iOS

Version

Which versions are you using:

  • react-native-swiper v1.6.0
  • react-native v0.63.2

Expected behaviour

No errors/warnings?

Actual behaviour

This code produces a warning, Warning: Cannot update a component from inside the function body of a different component..

import React, {useState} from 'react';

import Swiper from 'react-native-swiper';

const [swiperIndex, setSwiperIndex] = useState(0);

const testComponent = () => {
  return <Swiper style={styles.wrapper} onIndexChanged={(i) => setSwiperIndex(i)}>
}

export default testComponent;

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 37
  • Comments: 26

Most upvoted comments

I use onIndexChanged={(newIndex) => { setTimeout(() => { setIndex(newIndex); }, 1); }} The warning is gone.

Ugh, I just went away from this package and used react-native-snap-carousel instead. Sorry guys.

@vietdiemtran I recommend to use react-native-snap-carousel instead. It’s easy to use, and it works.

Same problem here, this is cased by react being updated to 16.13.0

This blog post about 16.13.0 says to wrap the setState call into useEffect. I’m not quite sure exactly what they mean or how to do that…

Same problem here, this is cased by react being updated to 16.13.0

Until there is a fix, you can do this nasty thing:

index.js

import { AppRegistry, LogBox } from 'react-native';

LogBox.ignoreLogs([
  'Warning: Cannot update a component from inside the function body of a different component',
]);

you can use useRef instead of state to store indexStep value

import React, { useState, useRef } from 'react'
import { View, Text, Image } from 'react-native'
// import { translate } from 'i18n'
import styles from "./styles";
import { translate } from '../../i18n';
import { Images } from 'themes'
import Slide1 from "./Slide1";
import Slide2 from "./Slide2";
import Slide3 from "./Slide3";
import Slide4 from "./Slide4";
import Swiper from 'react-native-swiper'

const TOTAL_NUMBER_OF_PAGES = 4

export default function Onboard() {
  const swiperRef = useRef()
  const currentStep = useRef(0);

  const onNext = () => {
    if (currentStep.current < TOTAL_NUMBER_OF_PAGES - 1) { 
      swiperRef?.current?.scrollBy(1); // go forward one page
      currentStep.current += 1
    } else {
      // navigation.navigate('Next Screen through navigation')
    }
  }

  const onIndexChanged = (ind) => {
    currentStep.current = ind
  }
  return (
    <View style={styles.container}>
      <Swiper
        style={styles.wrapper}
        showsButtons
        loop={false}
        ref={swiperRef}
        onIndexChanged={onIndexChanged}
      >
        <Slide1 onNext={onNext} />
        <Slide2 onNext={onNext} />
        <Slide3 onNext={onNext} />
        <Slide4 onNext={onNext} />
      </Swiper>
    </View>
  )
}

in my case, i fix by way onIndexChanged={(index) => setTimeout(() => { setIndex(index) }, 0) }

this fix is simple, I would do a PR myself but I no longer use this library as I ended up writing my own solution using a functional component which depends on a flatlist rather than using a scroll view. All that needs to be done to fix this is remove

UNSAFE_componentWillUpdate(nextProps, nextState) {
    // If the index has changed, we notify the parent via the onIndexChanged callback
    if (this.state.index !== nextState.index)
      this.props.onIndexChanged(nextState.index)
  }

and add

this.propsonIndexChanged(index)

to the updateIndex function between line 519 and 520

@vietdiemtran I recommend to use react-native-snap-carousel instead. It’s easy to use, and it works.

How to I give like 100 likes? 🥇

Thanks for the recommendation…good library, well abstracted and seems relatively stable.

any update on this issue ?

Just use setTimeout:

const updateIndex = React.useCallback((res) => {
        setTimeout(() => {
            setSelectedTab(res)
        })
    }, [])

I think React wants react-native-swiper to do something like

useEffect(() => {
  this.props.onIndexChanged && this.props.onIndexChanged(index)
}, [index])

But we have no control over it.

The library seems to be old, the code is written in an old fashioned way. Maybe it should be rewritten in a new React way.

Apparently its this here that causes it on line 213

UNSAFE_componentWillUpdate(nextProps, nextState) {
    // If the index has changed, we notify the parent via the onIndexChanged callback
    if (this.state.index !== nextState.index)
      this.props.onIndexChanged(nextState.index)
  }