Re-render flat-list in functional component

Viewed 2612

I have an empty array that I pass to my flat-list. I also use useEffect to fetch data from the server and update the list. However, after setting the new state of the array with the data, the flat-list is not re-rendered.

const [listData, setlistData] = React.useState<Transaction[]>([])
const [dataUpdated, setDataUpdated] = React.useState<boolean>(false)

React.useEffect(() => {

    if(route.params.showAllData)
    {
        fetchTransactions(1, TRANSACTION_PAGE_SIZE, 1)
            .then((res: Transaction) => {
                console.log(`TransactionsScreen: userEffect [] => fetched ${JSON.stringify(res)}`);
                setlistData(prevState => ({...prevState, ...res}));
                setDataUpdated(true)
            })
            .catch(err => {

                //TODO: handle error scenerio
                ToastAndroid.show(`Failed to fetch transacrions`, ToastAndroid.SHORT)
                
            })
    }}, [])    

<FlatList
     data={listData}
     renderItem={item => _renderItem(item)}
     ItemSeparatorComponent={TransactionListSeparator}
     extraData={dataUpdated} // extraData={listData <--- didn't work either}
     keyExtractor={item => item.id.toString()}/>

I tried to add the data array as extraData value but it didn't work, I also tried to add another boolean notifying that the data was updated but it didn't work either.

How can re-render the flat-list correctly?

4 Answers

I solved the problem this way :

useEffect(() => {
    setDataUpdated(!dataUpdated);
  }, [listData]);

NOTE : Your updating the dataUpdated to true directly. I do not recommend this, do it this way instead : setDataUpdated(!dataUpdated)

Plus ensure that all elements of the FlatList have a unique key, if not it will not re-render at any cost

Try this, make sure you are returning your child component and giving dependency array with useEffect upon which chnage you want to re-render your component

const [listData, setlistData] = React.useState<Transaction[]>([])
const [dataUpdated, setDataUpdated] = React.useState<boolean>(false)

React.useEffect(() => {

    if(route.params.showAllData)
    {
        fetchTransactions(1, TRANSACTION_PAGE_SIZE, 1)
            .then((res: Transaction) => {
                console.log(`TransactionsScreen: userEffect [] => fetched                  ${JSON.stringify(res)}`);
                setlistData(prevState => ({...prevState, ...res}));
                setDataUpdated(true)
            })
            .catch(err => {

                //TODO: handle error scenerio
                ToastAndroid.show(`Failed to fetch transacrions`,                              ToastAndroid.SHORT)
                
            })
    }}, [listData])  
    
    return (

  <FlatList
     data={listData}
     renderItem={item => _renderItem(item)}
     ItemSeparatorComponent={TransactionListSeparator}
     extraData={dataUpdated} // extraData={listData <--- didn't work either}
     keyExtractor={item => item.id.toString()}/>
     
     )

You can't spread the array as you did. For example, if you have two arrays.

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

And spread them like:

const arr3 = { ...arr1, ...arr2 };

The result will be:

{ 0: 4, 1: 5, 2: 6 }

Change curly braces to the square.

setlistData(prevState => ([...prevState, ...res]));

Since your FlatList expects data to be an array, not an object.

You can force flatlist to rerender by passing the updated list as an extraData prop, i.e extraData={listData}. However, when using functional components a common mistake is passing the same instance of the list data as the prop. This will not trigger a rerender even if the content in the list or the length of the list has changed. FlatList sees this as the same and will not rerender. To trigger a rerender you have to create an entirely new instance of the list.

Egs:

//This update will NOT trigger a rerender
const copy = listData;
copy.push(something)
setListData(copy)

////////
<FlatList extraData={listData}
.....
/>
//This WILL trigger a rerender
const copy = [...listData]
copy.push(something)
setListData(copy)

////////
<FlatList extraData={listData}
.....
/>
Related