Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a FlatList with scrollToEnd

I have two issues when using a FlatList (that both work with a ListView).

I have a list that is displayed via a display property for each item and then checked in my FlatList like so:

<FlatList
  ref='flatList'
  data={this.state.dataSource}
  enableEmptySections
  renderItem={({item}) => item.display && <Text>{item.text}</Text>} />

When I change the display property, it doesn't display however. This worked fine in the ListView setup.

My second problem is that scrollToEnd is firing an undefined error on "this._listref" which is within the scrollToEnd function. I pass no parameters to it.

setTimeout(this.refs.flatList.scrollToEnd, 0)

My questions are, how do I update the data in my array for the list so it changes in the list, and the second question is how do I fix the scrollToEnd error?

Update 1: (data removed, not needed anymore)

Update 2: Problem ONE solved, the list needs "extraData" comparison to see if it updates. The fixed edition:

<FlatList
  ref='flatList'
  data={this.state.dataSource}
  extraData={this.state}
  enableEmptySections
  renderItem={({item}) => item.display && <Text>{item.text}</Text>} />

It's a bit convoluted, but I believe extraData acts as a comparison to see if anything in that data set updates. If so, re-render. That problem is solved.

"undefined is not an object (evaluating 'this._listRef')" still not fixed

Update 3: Scroll function works IF taken outside the setTimeoutfunction

Update 4:

Setting the initial data array

  this.state = {
    dataSource: pageService(state.params.page),
    cursor: 0
  }

Setting display as true:

  renderNextModule() {
    let newCursor = this.state.cursor
    if (newCursor < this.state.dataSource.length-1) {

        newCursor++
        let newSource = this.state.dataSource
        newSource[newCursor].display = true
        this.setState({
            dataSource: newSource,
            cursor: newCursor
        })
    }
  }
like image 799
Organiccat Avatar asked Jan 03 '23 22:01

Organiccat


1 Answers

this._listRef is undefined because setTimeout calls the function and sets its context (this) to null.

As a workaround, you can actually use a wrapper function (here with ES6 syntax) :

setTimeout(() => this.refs.flatList.scrollToEnd(), 0)

Regarding your 1st problem, I don't think you're solving it the right way. You were right that it uses a shallow comparison, but you might not need the extraData prop.

Are you mutating your array directly? How are you updating the item.display property?


For your 1st problem,
Replace this line

let newSource = this.state.dataSource

By this

let newSource = this.state.dataSource.slice()

In JavaScript, arrays are copied by reference, which means that you were not cloning the array but mutating it ; and as FlatList is efficient (and this is for every PureComponent), when you want to re-render it (by setting the state like you did), it does a === comparison on its old and new props to see if anything changed.

As you were mutating the old array, 'oldArray === newArray', so it was deciding to not update again.

This was a bit concise and fast but you can read more about why you need to create a new array with .slice() here. This is true for arrays and objects in JavaScript.

like image 120
yachaka Avatar answered Jan 06 '23 10:01

yachaka