94

Unlike ListView we can update this.state.datasource. Is there any method or example to update FlatList or re-render it?

My goal is to update the text value when user press button ...

renderEntries({ item, index }) {
    return(
        <TouchableHighlight onPress={()=> this.setState({value: this.state.data[index].value+1})>
             <Text>{this.state.data[index].value}</Text>
        </TouchableHighlight>
    )
}

<FlatList 
    ref={(ref) => { this.list = ref; }} 
    keyExtractor={(item) => item.entry.entryId} 
    data={this.state.data} 
    renderItem={this.renderEntries.bind(this)} 
    horizontal={false} />
shalonteoh
  • 1,604
  • 1
  • 11
  • 15
  • 9
    The [docs for FlatList](https://facebook.github.io/react-native/docs/flatlist.html) say "This is a `PureComponent` which means that it will not re-render if props remain shallow- equal. Make sure that everything your `renderItem` function depends on is passed as a prop that is not `===` after updates, otherwise your UI may not update on changes. This includes the `data` prop and parent component state." Are you following this advice? – Jordan Running Apr 13 '17 at 18:38
  • 3
    No matter what I tried with `extraData` and `shouldItemUpdate`, I could not get the list to re-render. What I ended up doing was clearing out the state, waiting for that to render and then updating the state. `this.setState({ data: null }, () => { this.setState({ data: actualData }) });` – Joshua Pinter Jul 31 '17 at 04:23

18 Answers18

201

Use the extraData property on your FlatList component.

As the documentation states:

By passing extraData={this.state} to FlatList we make sure FlatList will re-render itself when the state.selected changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is also a PureComponent and the prop comparison will not show any changes.

HarshitMadhav
  • 3,687
  • 5
  • 33
  • 43
ED209
  • 2,226
  • 1
  • 12
  • 13
  • 38
    I am using redux and in that case sending data like `data={this.props.searchBookResults}` and neither `extraData={this.state}` nor `extraData={this.props}` is working for me. And `data={this.props.searchBookResults}` is not working too. What to do ? – Sujit Nov 02 '17 at 14:35
  • @EdwinVargas did you find any solution? I couldn't – Sujit Nov 27 '17 at 13:44
  • 11
    Use extraData: to refresh your `... data={this.props.searchBookResults} extraData={this.state.refresh} onPress={()={this.setState({ refresh: !refresh})}` – Nay Dec 08 '17 at 15:02
  • @Sujit,@EdwinVargas did you got any solution? – praj Dec 17 '17 at 06:08
  • 9
    I am struggling with the same problem. It doesn't matter what I pass on `extraData`, the component does not update :( – Denis Cappelini Dec 18 '17 at 11:51
  • passing `this.state` as `extradata` is kind of bad practice though. Use `this.state.datasource` – Thijs Steel Mar 26 '18 at 15:56
  • 2
    @DenisCappelini, make sure data passed into the `extraData` prop changes when child item[s] must re-render. For example, if list items can be active/inactive, make sure that status variable, whether part of the `this.state` object or `this.props` object is what should go inside `extraData`. It might be a cursor, or an array of active items, etc. – Emperor_Earth May 02 '18 at 18:32
  • 2
    @Sujit I was running into the same issue but then I realized that I had implemented the `shouldComponentUpdate()`, once I updated that or completely comment out, things were working just as described in the documentation. – JanithaR Jun 19 '18 at 09:30
  • For me list didn't rerender when data array was sorted another way - so items remained same and only their order was changed. Adding extraData did the trick. – Alex Green Sep 15 '18 at 02:06
  • How to update a single item in flatlist without re-rendering all flatlist items? – Iqbal Jan May 06 '19 at 20:54
  • @praj, found any solution for redux? – Thomas Stubbe Jun 12 '19 at 14:22
  • hello, Guys, I want to re-render Flatlist after deleting the last item, can check this Q https://stackoverflow.com/questions/58780167/i-cant-re-render-the-flatlist-after-empty-data – Oliver D Nov 09 '19 at 14:37
  • Thank you so much, this saved me hours of extra work!!! – Rayyan Mar 02 '21 at 13:38
  • @JanithaR - you are a genius!!....that was exactly my issue – james murphy Apr 13 '21 at 03:57
61

For quick and simple solution Try:

  1. set extra data to a boolean value.

    extraData={this.state.refresh}

  2. Toggle the value of boolean state when you want to re-render/refresh list

    this.setState({ 
        refresh: !this.state.refresh
    })
    
Hradesh Kumar
  • 1,593
  • 13
  • 18
  • That's exactly what I need since I'll be adding data to the data prop. Also, that is a super weird implementation by the React-Native team... it'd make way more sense to have a refresh function or property on data. IE: `this.data.refresh()`. Instead, we set a boolean and change it. The booleans value itself has no meaning, so what is the point in it existing?... (There is no point, other than making the data refresh right?) – Isaac C Way Oct 01 '19 at 18:24
  • @HradeshKumar I do it but the last item inside Data it's can't disappear ! – DevAS Nov 08 '19 at 12:16
  • this works like charm, but I still don't know why `this.state.users` wasn't working, even though I was changing a flag in some users. – shyammakwana.me Feb 26 '20 at 11:49
  • Doesn't work for me, can you please help : https://stackoverflow.com/questions/65547030/flatlist-does-not-rerender-even-if-state-assigned-to-extradata-changes – Gayatri Dipali Jan 03 '21 at 07:21
  • Hi, hope you are good, can you please help me out with this question? https://stackoverflow.com/questions/65769926/react-native-inserting-new-object-inside-array – kd12345 Jan 18 '21 at 13:52
  • Thanks for the answer. But the only problem here with me is that i want to get the state of another class. There is another class where there is a button and after clicking that button, i am also changing the state of that component. Now i want to send that state to its parent component where the flatList is actually rendering. Please show if you have answer – Irfan wani Jan 28 '21 at 10:27
24

Oh that's easy, just use extraData

You see the way extra data works behind the scenes is the FlatList or the VirtualisedList just checks wether that object has changed via a normal onComponentWillReceiveProps method.

So all you have to do is make sure you give something that changes to the extraData.

Here's what I do:

I'm using immutable.js so all I do is I pass a Map (immutable object) that contains whatever I want to watch.

<FlatList
    data={this.state.calendarMonths}
    extraData={Map({
        foo: this.props.foo,
        bar: this.props.bar
    })}
    renderItem={({ item })=>((
        <CustomComponentRow
            item={item}
            foo={this.props.foo}
            bar={this.props.bar}
        />
    ))}
/>

In that way, when this.props.foo or this.props.bar change, our CustomComponentRow will update, because the immutable object will be a different one than the previous.

SudoPlz
  • 14,037
  • 8
  • 67
  • 100
  • 3
    i finally understood `extraData`! thanks for the link to the `VirtualisedList` code! – Tushar Koul Oct 17 '18 at 21:41
  • hello,@SudoPiz I want to re-render Flatlist after deleting the last item, can check this Q https://stackoverflow.com/questions/58780167/i-cant-re-render-the-flatlist-after-empty-data – Oliver D Nov 09 '19 at 14:38
7

OK.I just found out that if we want the FlatList to know the data change outside of the data prop,we need set it to extraData, so I do it like this now:

<FlatList data={...} extraData={this.state} .../>

refer to : https://facebook.github.io/react-native/docs/flatlist#extradata

Mahdi Bashirpour
  • 9,916
  • 5
  • 70
  • 101
  • 2
    The best implementation... I preferred to set it as the same as the data prop, so it doesn't refresh when anything in the state changes, but this is the EASIEST implementation by far. Thanks! – Robert Kehoe Jan 29 '19 at 15:05
  • hello, @MahdiBashirpour I want to re-render Flatlist after deleting the last item, can check this Q https://stackoverflow.com/questions/58780167/i-cant-re-render-the-flatlist-after-empty-data – Oliver D Nov 09 '19 at 14:38
  • Hi, hope you are good, can you please help me out with this question? https://stackoverflow.com/questions/65769926/react-native-inserting-new-object-inside-array – kd12345 Jan 18 '21 at 13:52
  • hi Mahdi, can you please have a look at this question? https://stackoverflow.com/questions/65786115/react-native-updating-a-specific-item-inside-a-flatlist – kd12345 Jan 19 '21 at 12:57
4

If you are going to have a Button, you can update the data with a setState inside the onPress. SetState will then re-render your FlatList.

WilliamC
  • 161
  • 9
  • 3
    there is a setState in my touchablehighlight. the setState is working fine but the flatlist is not showing update of the state value -> this.state.value – shalonteoh Apr 13 '17 at 17:35
  • The values in your FlastList isn't updating because you aren't updating the data source directly. this.state.data is what is rendered with the FlatList, but in the onPress you are only updating this.state.value. – WilliamC Apr 13 '17 at 17:59
  • sorry my mistake, question typo. should be this.state.data[index].value – shalonteoh Apr 13 '17 at 18:09
4

after lots of searching and looking for real answer finally i got the answer which i think it is the best :

       <FlatList
      data={this.state.data}
      renderItem={this.renderItem}
      ListHeaderComponent={this.renderHeader}
      ListFooterComponent={this.renderFooter}
      ItemSeparatorComponent={this.renderSeparator}
      refreshing={this.state.refreshing}
      onRefresh={this.handleRefresh}
      onEndReached={this.handleLoadMore}
      onEndReachedThreshold={1}
      extraData={this.state.data}
      removeClippedSubviews={true}
      **keyExtractor={ (item, index) => index }**
              />
    .....

my main problem was (KeyExtractor) i was not using it like this . not working : keyExtractor={ (item) => item.ID} after i changed to this it worked like charm i hope this helps someone.

ramin azadi
  • 41
  • 1
  • 5
3

Just an extension on the previous answers here. Two parts to ensure, Make sure that you add in extraData and that your keyExtractor is unique. If your keyExtractor is constant a rerender will not be triggered.

<FlatList
data={this.state.AllArray}
extraData={this.state.refresh}
renderItem={({ item,index })=>this.renderPhoto(item,index)}
keyExtractor={item => item.id}
>
                                    </FlatList>
1

I solved this problem by adding extraData={this.state} Please check code below for more detail

render() {
    return (
      <View style={styles.container}>
        <FlatList
          data={this.state.arr}
          extraData={this.state}
          renderItem={({ item }) => <Text style={styles.item}>{item}</Text>}
        />
      </View>
    );
  }
nawaab saab
  • 1,783
  • 2
  • 17
  • 34
0

I have replaced FlatList with SectionList and it is updates properly on state change.

<SectionList
  keyExtractor={(item) => item.entry.entryId} 
  sections={section}
  renderItem={this.renderEntries.bind(this)}
  renderSectionHeader={() => null}
/>

The only thing need to keep in mind is that section have diff structure:

const section = [{
  id: 0,
  data: this.state.data,
}]
Jamland
  • 891
  • 13
  • 17
  • Hi, hope you are good, can you please help me out with this question? https://stackoverflow.com/questions/65769926/react-native-inserting-new-object-inside-array – kd12345 Jan 18 '21 at 13:52
0

For me, the trick was extraData and drilling down into the item component one more time

state = {
  uniqueValue: 0
}

<FlatList
  keyExtractor={(item, index) => item + index}
  data={this.props.photos}
  renderItem={this.renderItem}
  ItemSeparatorComponent={this.renderSeparator}
/>

renderItem = (item) => {
  if(item.item.selected) {
    return ( <Button onPress={this.itemPressed.bind(this, item)}>Selected</Button> );
  }
  return ( <Button onPress={this.itemPressed.bind(this, item)}>Not selected</Button>);
}

itemPressed (item) {
  this.props.photos.map((img, i) => {
    if(i === item.index) {
      if(img['selected') {
        delete img.selected;
      } else {
        img['selected'] = true;
      }
      this.setState({ uniqueValue: this.state.uniqueValue +1 });
    }
  }
}
Dazzle
  • 2,196
  • 3
  • 18
  • 40
0

Put variables that will be changed by your interaction at extraData

You can be creative.

For example if you are dealing with a changing list with checkboxes on them.

<FlatList
      data={this.state.data.items}
      extraData={this.state.data.items.length * (this.state.data.done.length + 1) }
      renderItem={({item}) => <View>  
goodhyun
  • 4,210
  • 3
  • 27
  • 23
0

If we want the FlatList to know the data change both prop and state,we can construct an object referencing both prop and state and refresh the flatlist.

const hasPropOrStateChange = { propKeyToWatch: this.props, ...this.state};
<FlatList data={...} extraData={this.hasPropOrStateChange} .../>

Docs: https://facebook.github.io/react-native/docs/flatlist#extradata

0

In react-native-flatlist, they are a property called as extraData. add the below line to your flatlist.

 <FlatList
          data={data }
          style={FlatListstyles}
          extraData={this.state}
          renderItem={this._renderItem}
       />
ravi
  • 204
  • 2
  • 10
0

In this example, to force a re-render, just change the variable machine

const [selected, setSelected] = useState(machine)

useEffect(() => {
    setSelected(machine)
}, [machine])
Bill Zelenko
  • 1,586
  • 1
  • 12
  • 21
0

I am using functional component, in that I am using Flatlist with redux data. I am managing all the state with Redux store. Here is the solution to update the Flatlist data after the api call.

I was first doing like this:-

const DATA  = useSelector((state) => state.address.address);


<FlatList
    style = {styles.myAddressList}
    data = {DATA}
    renderItem = {renderItem}
    keyExtractor = {item => item._id}
    ListEmptyComponent = {EmptyList}
    ItemSeparatorComponent={SeparatorWhite}
    extraData = {refresh}
    
    />

but the data was not re-rendering my Flatlist data at all.

As a solution I did like given Below:-

<FlatList
    style = {styles.myAddressList}
    data = {useSelector((state) => state.address.address)}
    renderItem = {renderItem}
    keyExtractor = {item => item._id}
    ListEmptyComponent = {EmptyList}
    ItemSeparatorComponent={SeparatorWhite}
    />

I am passing the Redux state directly to the Flatlist Datasource rather than allocating it to the variable.

Thank you.

Pravin Ghorle
  • 330
  • 2
  • 5
0
const [itemSelected, setItemSelected] = setState(null);
....
const FlatListItem = (item) => {
    return (
        <TouchableOpacity onPress={() => setItemSelected(item.id)}>
            <View style={ (itemSelected === item.id) ? style.itemWrapperActive : style.itemWrapper }>
                <Text>{ item.label }</Text>
            </View>
        </TouchableOpacity>
    )
}
....
<FlatList
    ItemSeparatorComponent={() => <View style={{ width: 20 }} />}
    data={ flatListData }
    renderItem={ ({item}) => FlatListItem(item) }
    keyExtractor={ (item) => item.id }
    extraData={ itemSelected }
/>
tarokins
  • 116
  • 2
  • 9
-1

Just use:

onRefresh={true}

inside your flatList component.

double-beep
  • 3,889
  • 12
  • 24
  • 35
-1

Flatlist's extraData wasn't working for me and I happened to be using a prop from redux. This sounded similar to issues from the comments in ED209's answer. I ended up manually calling setState when I receive the prop.

componentWillReceiveProps(nextProps: StateProps) {
    if (this.props.yourProp != null && nextProps.yourProp) {
        this.setState({ yourState: processProp(nextProps.yourProp) });
    }
}


<FlatList
  data={this.state.yourState}
  extraData={this.state.yourState}
/>

For those of you using > React 17 use getDerivedStateFromProps

Mr.Lee
  • 99
  • 2