Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple call to render item #478

Open
deepakaggarwal7 opened this issue Feb 12, 2019 · 27 comments
Open

Multiple call to render item #478

deepakaggarwal7 opened this issue Feb 12, 2019 · 27 comments

Comments

@deepakaggarwal7
Copy link

deepakaggarwal7 commented Feb 12, 2019

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

  2. I see a virtualized key warning message despite official docs mentioning that explicit key isn't required to be specified
    Image

@kdrich
Copy link

kdrich commented Feb 21, 2019

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

@bd-arc
Copy link
Contributor

bd-arc commented Feb 22, 2019

@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
Copy link
Author

@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.

@jsellam
Copy link

jsellam commented Feb 28, 2019

Same issue

@ethantran
Copy link

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}

@kirkdrichardson
Copy link

@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.

@ethantran
Copy link

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

@baeteja
Copy link

baeteja commented Apr 2, 2019

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?

@DavitVosk
Copy link

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

@kunalbhatt
Copy link

any update here?

@defigods
Copy link

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

@Foskas
Copy link

Foskas commented Sep 18, 2019

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

@minayaleon
Copy link

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); })(); }, []);

@beingArkReal
Copy link

Any update on this issues ?

@ebarahona
Copy link

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?

@maartenvandillen
Copy link

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?

@curtismenmuir
Copy link

curtismenmuir commented Nov 5, 2020

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})

@MarouaniALA
Copy link

Any update on this issues ?

@nericode
Copy link

Same issue, any solution ?

@ebarahona
Copy link

ebarahona commented Jan 16, 2021

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 =...
    }```

@nericode
Copy link

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

@Cactiw
Copy link

Cactiw commented May 3, 2021

I moved whole renderItem function to the new PureComponent (I use functional components, so I use React.memo)

import React from "react";

export default React.memo(() =>
    const daysToRender = [0, 1, 2, 3]
    return(
            <Carousel data={daysToRender} renderItem={
                     (props) => <ScrollPaneView item={props.item} index={props.index}/>
))

ScrollPaneView.js:

export default React.memo(({item, index} => {
    console.log("Rendering", index)
    return (
        <View><Text>Rendered</Text></View>
    )
}

And now renderItem is called only once for each item.

@ebarahona
Copy link

ebarahona commented May 3, 2021

React.memo((

Memory leak after memoizing? Are you using this on a more complex real-world use case? My render-item is a video component with a lot of metadata and nested child components

@Cactiw
Copy link

Cactiw commented May 3, 2021

React.memo((

Memory leak after memoizing? Are you using this on a more complex real-world use case? My render-item is a video component with a lot of metadata and nested child components

According to React docks, React.memo is a higher order component. It’s similar to React.PureComponent but for function components instead of classes.; and it keeps in memory only current version of PureComponent, and checks the component parameters in order to understand whether the component needs to be re-rendered. So there should be no memory leak. However, the item itself in my code is quite simple, and it would be good to test it on more complex elements.

@baymaxdarwin
Copy link

baymaxdarwin commented Jun 13, 2021

Hi @ebarahona , if you found an optimal solution for video content. can you please share with us. I'm also facing some issues while rendering video content with snap-carousal.

@ebarahona
Copy link

Hi @ebarahona , if you found an optimal solution for video content. can you please share with us. I'm also facing some issues while rendering video content with snap-carousal.

I have a working "solution" not an elegant one but it's currently working. I can provide you with some help, reach out via email, same username at gmail

@dohooo

This comment was marked as spam.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests