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

onVisibleIndicesChanged stops working after X list items [Android and iOS] #334

Open
pribeh opened this issue Apr 25, 2019 · 4 comments
Open

Comments

@pribeh
Copy link

pribeh commented Apr 25, 2019

On a recyclerlistview, we're noticing that after the first 8 items are loaded each subsequent item no longer fires onVisibleIndicesChanged (on both iOS and Android).

We are noticing a few funny things using full screen width and height for list items so we're not sure if that might impact onVisibleIndicesChanged or not. Any tips for debugging would be most welcome. Why eight? we don't know. Sometimes it'll start working again but more often than not it won't fire after the first 8.

The list we have is designed to be a vertical swiping experience. Each item is swiped to by the user. This is to accommodate full screen captured video and images.

Data/layout provider

	@computed get _dataProvider() {
		return this.dataProviderSchema.cloneWithRows(this.props.MainStore.userPosts)
	}

	_layoutProvider = new LayoutProvider(
		(index) => {
			return 'LIST';
		},
		(type, dim, index) => {
			switch (type) {
				case 'LIST':
					dim.width = screen.width;
					dim.height = screen.height;
					break;
				default:
					dim.width = 0;
					dim.heigh = 0;
			}
		}
	)

	dataProviderSchema = new DataProvider((activity1, activity2) => {
		return activity1._id != activity2._id
	})

OnVisibleIndicesChanged logic:

	//triggered when visible posts in list changes
	_onVisibleIndicesChanged = (all, now, notNow) => {
		if (notNow && notNow.length > 0 && this._dataProvider._data[notNow[0]].type.startsWith("video")) {
			const pauseVideoFunction = this.activityComponentVideoPauseFunc[notNow[0]];
			if (pauseVideoFunction) pauseVideoFunction();
		} 

		for (const index of now) {	
			if (this._dataProvider._data[index].type.startsWith("video")) {
				const startVideoFunction = this.activityComponentVideoRunFunc[index];
				if (startVideoFunction) startVideoFunction();
			} else if (this._dataProvider._data[index].type === "image/png") {
				logEvent("view_post", {
					"type": "image",
					"post_id": this._dataProvider._data[index]._id
				})
			}
		}
		if (notNow && notNow.length > 0 && this._dataProvider._data[notNow[0]]) {
			const closeBoardFunction = this.activityComponentBoardCloseFunc[notNow[0]];
			if (closeBoardFunction) closeBoardFunction();
		} 
	}

Rowrenderer (the activitycomponent has height:screen.height set).

	rowRenderer = (type, item, index) => {
		return (
			<Animatable.View style={{ flex: 1, }} duration={150} useNativeDriver={true} animation="fadeInUp">
				<ActivityComponent
					activity={item}
					onCommentsPress={() => this._onPressComments(item)}
					onPressProfile={() => this._onPressProfile(item)}
					onDelete={(postId) => this._onDelete(postId)}
					lineCount={this.lineCount[index]}
					pauseVideo={this.pauseVideo}
					startVideo={this.startVideo}
					rowIndex={index}
					gemsBarOptionsListClosed={this.closeBoard}
				/>
			</Animatable.View>
		)
	};

List

<View style={{flex:1}}>
	<RecyclerListView
		onEndReached={this._onEndReached}
		dataProvider={this._dataProvider}
		layoutProvider={this._layoutProvider}
		rowRenderer={this.rowRenderer}
		onVisibleIndicesChanged={this._onVisibleIndicesChanged}
		ref={list => (this.props.MainStore.todayListRef = list)}
		snapToInterval={screen.height}
		decelerationRate={0.83}
		snapToAlignment={"center"}
		automaticallyAdjustContentInsets={true}
		overScrollMode="never"
		contentInset={{ top: - safeAreaInsetTop, left: 0, bottom: 0, right: 0, }}
		contentOffset={{ x: 0, y: safeAreaInsetTop }}
	/>
</View>

Notable libs

"mobx": "^5.9.4",
"mobx-react": "^5.4.3",
"react": "^16.8.6",
"react-native": "^0.59.6",
"react-native-animatable": "^1.3.2",
"react-native-gesture-handler": "^1.1.0",
"react-native-navigation": "^2.18.2",
"recyclerlistview": "^2.0.0-beta.4",
@pribeh pribeh changed the title onVisibleIndicesChanged stops working after 8 list items [Android and iOS] onVisibleIndicesChanged stops working after X list items [Android and iOS] Apr 25, 2019
@naqvitalha
Copy link
Collaborator

Will it be possible to provide a repro on expo? That will help me look into this quickly. On my end it doesn't happen for such items. There was a recent bug discovered to items with 0 height but that doesn't seem relevant here. If you want to debug yourself look inside ViewabilityTracker module. And see why the events are not being raised.

@peacechen
Copy link

I observed skipped onVisibleIndicesChanged events in a library that has complex full-page items rendered in RecyclerListView. I suspect it may be due to performance issues when the JavaScript bridge is saturated and delays scroll updates too much.

The progression of debugging:
I started with an unoptimized, complex full page component. These were the data rows rendered in RecyclerListView (in my case columns because the ScrollView was set to horizontal).

  • A fast iPad Pro does not miss events.
  • A fast Pixel 3 does not miss events.
  • A slow Android Nexus 6 misses events.
  • The Android simulator occasionally misses events (it isn't the fastest).

I optimized the components via shouldComponentUpdate and other techniques. With the more efficient code, the simulator has not missed events so far. The slow Nexus 6 still misses events unfortunately. While this could be an Android issue, the Pixel 3 seems to rule that out since it works well. If there was a slow enough iOS device, it likely would miss events too.

@paddy57
Copy link

paddy57 commented Aug 27, 2020

@naqvitalha onVisibleIndicesChanged triggers two times if scroll, I have 10 items in the array and I have full page item, I have set forceNonDeterministicRendering={true},

<RecyclerListView
      layoutProvider={this._layoutProvider}
      dataProvider={this.state.dataProvider}
      rowRenderer={this._rowRenderer}
      isHorizontal={true}
      pagingEnabled={true}
      onVisibleIndicesChanged={this._onVisibleIndicesChanged}
      forceNonDeterministicRendering={true}
    />
 this._layoutProvider = new LayoutProvider(
      index => {
        return ViewTypes.FULL
      },
      (type, dim) => {
        switch (type) {
          case ViewTypes.HALF_LEFT:
            dim.width = width / 2;
            dim.height = 160;
            break;
          case ViewTypes.HALF_RIGHT:
            dim.width = width / 2;
            dim.height = 160;
            break;
          case ViewTypes.FULL:
            dim.width = 100;
            dim.height = 100;
            break;
          default:
            dim.width = 0;
            dim.height = 0;
        }
      }
    );
_rowRenderer(type, data, index) {
  
   return (
     <View style={{ backgroundColor: index % 2 === 0 ? 'blue' : 'red', width: Dimensions.get("window").width , height: (Dimensions.get("window").height)  }}><Text>Data: {data}</Text></View>
   )
 }
_onVisibleIndicesChanged = (all, now, notNow) => {
    console.log('_onVisibleIndicesChanged',all, now, notNow)
  }

console logs

_onVisibleIndicesChanged (5) [0, 1, 2, 3, 4] (5) [0, 1, 2, 3, 4]
_onVisibleIndicesChanged [0] []

@AkshatBhaskar03
Copy link

Hey @paddy57 did you get any solution for two time console? Also do you know how can we achieve this case : "I want those indexes which is visible atleast more than 50%"

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

5 participants