6
votes

What am I doing ?

Trying to render a FlatList based on some items stored in state.data. A button is present which upon press appends a new item in state.data.

What is the problem ?

Upon pressing the button, I'm expecting that the renderItem to be called only state.data.length number of times whereas it's being called 2*state.data.length number of times. Also, this 2x behaviour is not always consistent and changes as state.data.length increases.

Ex: when state.data.length=3, then on initial render, number of times renderItem is called exactly 3 times which is same as the number of items in state.data and upon pressing button which appends a new item to state.data and now state.data.length=4 and renderItem is called 8 times i.e. 2*state.data.length times.

But, when state.data.length=11, then on initial render, renderItem is called exactly 21 times and upon pressing button which appends a new item to state.data and now state.data.length=12 and renderItem is called 23 times times which deviates from 2*state.data.length behaviour.

enter image description here

What am I expecting ?

renderItem to be called only state.data.length number of times on initial and subsequent renderings.

What I've tried ?

  1. Created a new project from scratch to test this behaviour with no luck.
  2. Made component inside renderItem PureComponent. Still the same behaviour as mentioned.
  3. Made a CodeSandbox at https://codesandbox.io/s/react-native-nn73t with react-native-web with the same behaviour as before. Please refer this sandbox for the code.
  4. Using props such as maxToRenderPerBatch, initialNumsToRender etc but to no avail either. Though using them with relatively large state.data does bring down the number of times renderItem is called but it's still not that great decrease.
  5. Referred similar questions but couldn't get much clarity

The real issue I'm facing is when I'm trying to render chat messages (fetching through API call and setting it inside the state) inside a FlatList. Right now there are about ~200 messages for which the renderItem is being called in a range of 800-1200 times which is taking a hit on the performance.

Is this behaviour which is deviating from my expectation expected ? If yes, then what's the logic behind this. If no, then what am I doing wrong here ?

3

3 Answers

4
votes

I went through your code and tried understanding your concern.

(renderItem ~ FlatList's prop ) Note :- when using flatlist your item of list should be pure component or should implement shouldComponentUpdate or else they will render a lot more time than expected

So the below points are keeping in mind that your flatlist item are implemented as stated.

  1. As per you concern of performance , there wont be any issue though the renderItem is called so many times . When you check how many times you item is actually rendered (by console.log() in your items render) that behaviour is as expected. so your items are not actually getting rendered it is only the renderItem in flatList that is called so many times.

try pressing (Add Item Immutably text) in the project link shared you will understand better. Check This project.

  1. So now the question which remains is why renderItem is called unexpected number of times and how to understand what is happening under the hood ?

Well even i am unaware of how things are implemented but as per what i read in docs i will just share few content that may help.

"In order to constrain memory and enable smooth scrolling, content is rendered asynchronously offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see blank content. This is a tradeoff that can be adjusted to suit the needs of each application, and we are working on improving it behind the scenes."

and this is default value of few props which helps in controlling the tradeoff

maxToRenderPerBatch: 10,

onEndReachedThreshold: 2, // multiples of length

scrollEventThrottle: 50,

updateCellsBatchingPeriod: 50,

windowSize: 21, // multiples of length

for understanding more clearly , we need to understand how VirtualizedList is implemented. I will surely update this answer after digging more.

0
votes

Had the same issue, my problem was passing arrow functions as props, I managed to fix this by replacing all my fnProp={() => {}} for fnProp={someFunc}

Another solution is to use shouldComponentUpdate. Being "YourWrappedItem" the item you are using as renderItem prop in FlatList, you could use a wrapper component like this:

class ItemToRender extends Component {
    shouldComponentUpdate() {
        return false
    }
    render() {
        return (
            <YourWrappedItem {...someProps}>
                Example
            </YourWrappedItem>
        )
    }
}

Hope it helps someone.

0
votes

Use React.memo for solve this issue

<FlatList
renderItem={({item, index}) => <DisplayRenderMemo item={item} index={index} onPress = { async() =>  { } }/>  } 
/>

const DisplayRenderMemo = React.memo(({ item, index, onPress }) => 
 {   
 const count = React.useRef(0);
  return ( 
    <TouchableOpacity 
                    key={index}   
                    onPress={onPress}>
                   <Text>{item.label} {(count.current++)} counts</Text>
    </TouchableOpacity> 
     )
 }, (prevProps, nextProps) => { 
console.log(nextProps, prevProps);
  return nextProps.item.data_id === prevProps.item.data_id 
})

First check without using the React.memo like

const DisplayRenderMemo = ({ item, index, onPress }) => 
     {   
     const count = React.useRef(0);
      return ( 
        <TouchableOpacity 
                        key={index}   
                        onPress={onPress}>
                       <Text>{item.label} {(count.current++)} counts</Text>
        </TouchableOpacity> 
         )
     }

Now it display like 0, 1, 2, 3 counts

Then use the React.memo like above mentioned code

Hurray!!! Now it display only the 0 counts. So we got the solution. It's not re-rendered.

Note: Please make sure which field is unique in {item} data. Because we can't use the index as unique. So just pass any unique key with your {item} data. The reason is If we getting the new data set for replacing the exist data, that exist data set controlled with react.memo by the index. So that same indexed item is not re-render for viewing in the flatlist