React-native-snap-carousel: Multiple call to render item

Created on 12 Feb 2019  路  21Comments  路  Source: meliorence/react-native-snap-carousel

It's a questiob but may turn out to be a bug.

Problem: _renderItems gets called thrice
How to replicate:

Following is the code to display a carousel in a very simple example:

import React, {PureComponent} from 'react'
import { ScrollView, Text, View, SafeAreaView,Platform, Dimensions, StyleSheet  } from 'react-native';
import Carousel from 'react-native-snap-carousel';

export default class SnapDemo extends PureComponent {

    constructor(props){
        super(props)
         this.state = {data: [{'num':1},{'num':2},{'num':3}]}
    }

    _renderItem (item) {
        console.log(item)
        return(<View key={item.item.num} style={{backgroundColor:'orange', height:100}}><Text>{item.item.num}</Text></View>)
    }

    render(){
       return(
            <SafeAreaView style={{height: 600}}>
           <Carousel
                  data={this.state.data}
                  renderItem={this._renderItem}
                  itemWidth={Dimensions.get('window').width * 0.85}
                  sliderWidth={Dimensions.get('window').width}
                  containerCustomStyle={{flex:1}}
                  removeClippedSubviews={true}
                  keyExtractor={(item, index) => index.toString()}
                  slideStyle={{ flex: 1 }}/>

        </SafeAreaView>
        )
    }
}

There are 2 problems:

  1. RenderItems is getting called thrice
    Image
  1. I see a virtualized key warning message despite official docs mentioning that explicit key isn't required to be specified
    Image

Most helpful comment

I have a similar problem. The renderItem gets triggered multiple times for the same index. It also triggers the renderItem with index 0 up to four times - no idea why this is happening

<Carousel
lockScrollWhileSnapping
ref={(c) => {
this.carousel = c;
}}
windowSize={1}
initialNumToRender={1}
maxToRenderPerBatch={1}
data={this.props.articles}
sliderWidth={this.props.screenWidth}
itemWidth={this.props.screenWidth}
itemHeight={this.props.screenHeight}
keyExtractor={(item, index) => item.article+ '-' + index}
scrollEnabled={this.isListMode() && this.state.isArticleLoaded}
onSnapToItem={this.onItemSnap}
renderItem={(params) => renderCorrectArticleDetail(params)}
/>

Even with initialNumToRender={1} and maxToRenderPerBatch={1} it still triggers renderItem 12 times. This is causing big performance issues for me. Any ideas?

All 21 comments

I have experienced this behavior as well. Here is a snack that reproduces this issue with static data.

@deepakaggarwal7 @kdrich Does the problem still occur if you simply replace Carousel with FlatList (the required props are the same) and add horizontal={true}?

Don't forget the following: import { FlatList } from 'react-native';.

@deepakaggarwal7 @kdrich Does the problem still occur if you simply replace Carousel with FlatList (the required props are the same) and add horizontal={true}?

Don't forget the following: import { FlatList } from 'react-native';.

RenderItem with a FlatList gets called once only for every iterated item.

Same issue

Ran into this issue as well. The fix is to make your key extractor more unique by combine your item key with the index. So something like keyExtractor={(item, index) => item.id+"_"+index}

@ethantran, it does look like keyExtractor is passed through to the Carousel component, though I don't see any change in behavior adding a more specific keyExtractor similar to what you reference above.

You can see an example here

The logs cumulate the total number of times each item is rendered. It is still three.

I see. The keyExtractor fixed problem 2, the warning message, for me. I am not sure how to fix the multi-render except using a pure component. Example

I have a similar problem. The renderItem gets triggered multiple times for the same index. It also triggers the renderItem with index 0 up to four times - no idea why this is happening

<Carousel
lockScrollWhileSnapping
ref={(c) => {
this.carousel = c;
}}
windowSize={1}
initialNumToRender={1}
maxToRenderPerBatch={1}
data={this.props.articles}
sliderWidth={this.props.screenWidth}
itemWidth={this.props.screenWidth}
itemHeight={this.props.screenHeight}
keyExtractor={(item, index) => item.article+ '-' + index}
scrollEnabled={this.isListMode() && this.state.isArticleLoaded}
onSnapToItem={this.onItemSnap}
renderItem={(params) => renderCorrectArticleDetail(params)}
/>

Even with initialNumToRender={1} and maxToRenderPerBatch={1} it still triggers renderItem 12 times. This is causing big performance issues for me. Any ideas?

Any progress here? I get the same problem. renderItem calls 10+ time

any update here?

same problem here.

hint to get rid out of this issue
use index value from the item.

change
{item, index} => index === 0
to
{item} => item.index === 0

Any idea how to solve this ? @seniordev32 solution doesn't work

For me, works with theses steps:

  • Add attr key={item.id}, for each render
  • Add const _keyExtractor = item => ${item.id};

<Carousel keyExtractor={_keyExtractor} />

For API request, you must use:

  • async componentDidMount()
  • useEffect
    useEffect(() => { // Using an IIFE (async function getRecords() { const response = await callToAPI(); setRecords(response.data); })(); }, []);

Any update on this issues ?

I have a similar problem. The renderItem gets triggered multiple times for the same index. It also triggers the renderItem with index 0 up to four times - no idea why this is happening

<Carousel
lockScrollWhileSnapping
ref={(c) => {
this.carousel = c;
}}
windowSize={1}
initialNumToRender={1}
maxToRenderPerBatch={1}
data={this.props.articles}
sliderWidth={this.props.screenWidth}
itemWidth={this.props.screenWidth}
itemHeight={this.props.screenHeight}
keyExtractor={(item, index) => item.article+ '-' + index}
scrollEnabled={this.isListMode() && this.state.isArticleLoaded}
onSnapToItem={this.onItemSnap}
renderItem={(params) => renderCorrectArticleDetail(params)}
/>

Even with initialNumToRender={1} and maxToRenderPerBatch={1} it still triggers renderItem 12 times. This is causing big performance issues for me. Any ideas?

Are you changing state in renderItem?

I'm changing state in onSnapToItem (to save the current item) but this rerenders the carousel which in turn jumps back to the index specified in firstItem. Any solution to this?

Couple of points:

  • Update your _renderItem() function so that it is returning a component (EG Move the returned View + Text to its own list-item component and pass details through props). This allows you to implement shouldComponentUpdate() lifecycle event in list-item to ensure that re-renders only happen when required. You may still see _renderItem() being called, but the render function in list-item component will not be re-rendered if shouldComponentUpdate() is configured correctly.

  • keyExtractor uses an anonymous function in render(), move this logic to outside of the render function (EG keyExtractor={this._keyExtractor})

Any update on this issues ?

Same issue, any solution ?

Same issue, any solution ?

Try using a pure component. Here is a barebones example, you can abstract the pure component to it's own file

```

const carouselComponent = (props) => {
const [selected, setSelected] = useState(null);
const [data, setData] = useState(props.data);

        // THIS IS THE PURE PART 
        const Item = ({ item, onPress, style }) => (
            <TouchableOpacity
                key={item.id}
                onPress={onPress}
                style={[styles.itemStyle, style]} 
            >
                <View style={styles.item}>
                      <Text style={styles.name}>
                        {item.name}
                      </Text>
                      <Image
                        style={styles.image}
                        source={{
                          uri: item.image,
                        }}
                      />
                </View>
            </TouchableOpacity>
          );
          // END OF PURE

          // RENDER ITEM    
          const renderItem = ({ item }) => {
            const selectedStyle = item.id === selectedId ? SELECTED : NONSELECTED;

            return (
              <Item
                item={item}
                onPress={() => handlePress(item)}
                style={{ selectedStyle }}
              />
            );
          };

            return(
                <View>
                    <Carousel
                        ref={myCarousel}
                        data={data}
                        renderItem={renderItem}
                        keyExtractor={(item) => item.id}
                        extraData={selectedId}
                        removeClippedSubviews={true}
                      />
                <View>
            )


// const styles =...
}```

@ebarahona Yes, my item is a PureComponent, but the render is call many

Was this page helpful?
0 / 5 - 0 ratings