React-native: FlatList ScrollView Error on any State Change - Invariant Violation: Changing onViewableItemsChanged on the fly is not supported

Created on 31 Dec 2017  路  11Comments  路  Source: facebook/react-native

Is this a bug report?

Yes

Have you read the Contributing Guidelines?

Yes

Environment

Environment:
OS: macOS High Sierra 10.13.1
Node: 6.11.4
Yarn: 1.1.0
npm: 5.3.0
Watchman: Not Found
Xcode: Xcode 9.2 Build version 9C40b
Android Studio: 2.3 AI-162.4069837

Packages: (wanted => installed)
react: 16.0.0 => 16.0.0
react-native: 0.51.0 => 0.51.0

Steps to Reproduce

  1. Please refer to snack
  2. Repo has also been uploaded at github
  3. Any state change produces an error when using onViewableItemsChanged
  4. What does this error even mean?

_Note: Placing the onViewableItemsChanged function in a const outside the render method also does not assist..._

           <FlatList
                    data={this.state.cardData}
                    horizontal={true}
                    pagingEnabled={true}
                    showsHorizontalScrollIndicator={false}
                    onViewableItemsChanged={(info) =>console.log(info)}
                    viewabilityConfig={{viewAreaCoveragePercentThreshold: 50}}

                    renderItem={({item}) =>
                        <View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
                            <Text>Dogs and Cats</Text>
                        </View>
                    }
                />

Expected Behavior

I'm not sure why this is happening. I would like to know if this is a bug with the current release, of if I'm doing something wrong.

Should users be using a separate workaround (ie. using onScroll rather than onViewableItemsChanged for information on scroll Position?

What does this error even mean?

Actual Behavior

Error
image

Reproducible Demo

Please refer to snack
Repo has also been uploaded at github

Locked

Most helpful comment

@njho I believe you'll need to set both the onViewableItemsChanged and the viewabilityConfig props as class properties, rather than inside the render() method.

E.g

export default class App extends Component<{}> {

    constructor(props) {
        super(props);
        this.state = {
            cardData: [{name: 'Clean Up the Oceans'}, {name: 'three'}],

        }

        this.handleViewableItemsChanged = this.handleViewableItemsChanged.bind(this)
        this.viewabilityConfig = {viewAreaCoveragePercentThreshold: 50}
    }

    handleViewableItemsChanged(info) {
        console.log(info)
    }

    render() {
        return (
            <View style={styles.container}>
                <Button title="Some State Change" onPress={()=>this.setState({...this.state, stateChange: 'someStateChange'})}/>
                <FlatList
                    data={this.state.cardData}
                    horizontal={true}
                    pagingEnabled={true}
                    showsHorizontalScrollIndicator={false}
                    onViewableItemsChanged={this.handleViewableItemsChanged}
                    viewabilityConfig={this.viewabilityConfig}

                    renderItem={({item}) =>
                        <View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
                            <Text>Dogs and Cats</Text>
                        </View>
                    }
                />
            </View>
        );
    }
}

Working Snack example: https://snack.expo.io/rJp1MLd7f

All 11 comments

It looks like this is not an issue in 0.49.3 as per a comment found here: react-native-calendars

@njho I believe you'll need to set both the onViewableItemsChanged and the viewabilityConfig props as class properties, rather than inside the render() method.

E.g

export default class App extends Component<{}> {

    constructor(props) {
        super(props);
        this.state = {
            cardData: [{name: 'Clean Up the Oceans'}, {name: 'three'}],

        }

        this.handleViewableItemsChanged = this.handleViewableItemsChanged.bind(this)
        this.viewabilityConfig = {viewAreaCoveragePercentThreshold: 50}
    }

    handleViewableItemsChanged(info) {
        console.log(info)
    }

    render() {
        return (
            <View style={styles.container}>
                <Button title="Some State Change" onPress={()=>this.setState({...this.state, stateChange: 'someStateChange'})}/>
                <FlatList
                    data={this.state.cardData}
                    horizontal={true}
                    pagingEnabled={true}
                    showsHorizontalScrollIndicator={false}
                    onViewableItemsChanged={this.handleViewableItemsChanged}
                    viewabilityConfig={this.viewabilityConfig}

                    renderItem={({item}) =>
                        <View style={{width: width, borderColor: 'white', borderWidth: 20,}}>
                            <Text>Dogs and Cats</Text>
                        </View>
                    }
                />
            </View>
        );
    }
}

Working Snack example: https://snack.expo.io/rJp1MLd7f

Excellent! Thank you @adrianfalleiro!

If you care to explain why this is the case, I'd be interested as well!

Thanks for the help.

@njho Both the viewabilityConfig prop and the onViewableItemsChanged are compared with their previous values in the componentWillReceiveProps() method of the FlatList component. This is done using the triple equals symbol.

Since you are using an inline arrow function and an inline object literal, these are re-created on each render. This means that the exception will be thrown on each render since the reference of the function or object is different each time.

I don't know what purpose the checks serve, I just took a peek at the source code haha.

https://github.com/facebook/react-native/blob/master/Libraries/Lists/FlatList.js#L430-L451

I have had this problem for a long time... gave up...

The new error changes it to explain about the 'key' property, but there IS NO KEY PROPERTY in the documentation. I changed:

to the following: (Added: key = {this.state.slideNum}).. Now it works!
<FlatList .... key = {this.state.slideNum} numColumns={this.state.slideNum} >

I tried with a class based object using always the same object viewabilityConfig and the same fonction onViewableItemsChanged every render but I still have this error:

Invariant Violation: Changing viewabilityConfig on the fly is not supported

I tripled checked and my viewabilityConfig object never changes ...

I fixed it and probably know the problem. PUt up your code where the problem is.

BTW - Here was my problem.... I was trying to use ES6 conventions and that does not work! As the clue from the previous author stated, your viewabilityConfig was being recreated each time. Should look like this in the parameters of the flatlist:
viewabilityConfig={this.viewabilityConfig}
not

viewabilityConfig={(i, v)=> this.viewabilityConfig(i, v)}

(or like that). In my constructor, the following exists:

this.handleViewableItemsChanged = this.handleViewableItemsChanged.bind(this)
this.viewabilityConfig = {viewAreaCoveragePercentThreshold: 90}

I have the same problem,
here is my code:

constructor(props) {
        super(props);
        this.state = {
            timeline: [],
        };
        this.handleViewableItemsChanged = this.handleViewableItemsChanged.bind(this);
        this.viewabilityConfig = {
            itemVisiblePercentThreshold: 50,
            minimumViewTime: 1,
        };
    }

handleViewableItemsChanged(info) {
        console.log(info)
    }

render() {
        return (
            <Container>
                <Content>
                    <FlatList
                        key={this.state.key}
                        onViewableItemsChanged={this.handleViewableItemsChanged}
                        viewabilityConfig={this.viewabilityConfig}
                        data={this.state.timeline}
                        renderItem={({item}) =>
                            <View style={{height:300, borderColor: 'white', borderWidth: 20,}}>
                                <Text>Dogs and Cats</Text>
                            </View>
                        }
                    />
                </Content>
            </Container>
        );
    };

handleViewableItemsChanged method not working (there is no log) while scrolling or stopping it

ERROR: Invariant Violation: numColumns does not support horizontal. Can you fix it?

@FZNsID
Can you provide some part of your code?

    data={ this.state.dataSource }
    horizontal={true}
    renderItem={({item}) => 

        <View style={{width: 360, height: 120.5, flex: 1, flexDirection: 'column', alignItems: 'center', paddingLeft: 10}}>
   <Image source = {{ uri: 'http://mypustak_new.s3.amazonaws.com/uploads/books/thumbs/'+item.thumb }} style={{width: 35.7, height: 45, marginTop: 8.7, alignItems: "center"}} />
          <View style={styles.cv1}>
          <Text style={styles.textView} >{item.category}</Text>
          <Text onPress={this.GetItem.bind(this, item.title)} style={styles.textView} >{item.title}</Text>
          <Text  style={styles.textView} >{item.author}</Text>
          </View>
        </View>

      }

    keyExtractor={(item, index) => index.toString()}
    numColumns={4}

    />
Was this page helpful?
0 / 5 - 0 ratings