React-native-draggable-flatlist: Getting many "Excessive number of pending callbacks: 501" errors

Created on 8 Jan 2020  路  14Comments  路  Source: computerjazz/react-native-draggable-flatlist

Just migrated from 1.1.7 to 2.0.10, and now (mostly triggered by scrolling in my experience so far) I'm getting this warning:

Warning: Please report: Excessive number of pending callbacks: 501. Some pending callbacks that might have leaked by never being called from native code: {
"66781":{"module":"ReanimatedModule","method":"getValue"},
"66784":{"module":"ReanimatedModule","method":"getValue"},
"66787":{"module":"ReanimatedModule","method":"getValue"},
"66791":{"module":"ReanimatedModule","method":"getValue"},
"66794":{"module":"ReanimatedModule","method":"getValue"},
"66797":{"module":"ReanimatedModule","method":"getValue"},
"66800":{"module":"ReanimatedModule","method":"getValue"}, ...

I'm using

"react-native": "0.61.4",
"react-native-reanimated": "~1.4.0",
"react-native-gesture-handler": "~1.5.0",

with expo sdk 36
This is my list setup:

<DraggableFlatlist
  onRef={ref => (this.tasksList = ref)}
  style={{ flex: 1 }}
  data={tasks}
  initialNumToRender={10}
  keyExtractor={task => `${task.id}`}
  renderItem={this.renderItem}
  extraData={this.props}
  keyboardShouldPersistTaps="handled"
  onRefresh={this.onRefresh}
  refreshing={refreshing}
  ItemSeparatorComponent={() => <ItemSeparator projectView={projectView} />}
  onEndReached={this.fetchNextPage}
  onEndReachedThreshold={0.5}
  ListFooterComponent={this.renderFooter}
/>

...

renderItem = ({ item, index, drag, isActive }) => {
  const props = {
    key: item.id,
    task: item,
    onLongPress: drag,
    index,
    ...otherPropsOmittedForClarity
  }
  return (
    <View
      style={isActive ? styles.draggingRow : {}}
      pointerEvents={isActive ? 'none' : 'auto'}
    >
      <TaskRow {...props} />
    </View>
  )
}

Not sure if it's related, but I am also getting a bunch of ## can't measure (key) reason: no ref for tasks after the initial 10 (I log the key if we're trying to measure), and if I remove initialNumToRender I get in between 20-25 before the "can't measure" shows up.

The list items are variably sized tasks, where TaskRow is a wrapping an animated View with gesture handlers for swiping left/right. Other than that, the items are pretty straightforward, I don't think I'm doing anything novel that warrants weird behavior like excessive pending callbacks.

Happy to provide more details as needed, I'm a huge reanimated fan so I'm very excited to see this library be written with it. Thanks for all your hard work!

Most helpful comment

@computerjazz not sure why I made it way more complicated than it had to be with the above solutions, I passed an "listIndex" where the index passed to the item is keyToIndex.get(itemKey) || listIndex

All 14 comments

Thanks for the writeup, if you can provide a repro snack that would be awesome.

There was also an animation bug when row items were not all the same size -- I just released 2.0.11 which should fix it. However, the bug was related to calculating which row is currently hovered-over and doesn't sound related to what you describe above.

Also, I recently released a swipeable item component that may be useful to you: https://github.com/computerjazz/react-native-swipeable-item/blob/master/README.md

@computerjazz hoping to get you a snack repro by tuesday, otherwise it'll be at some point next week. I'll check out your swipeable item library, thanks!

@computerjazz found this issue so I'm trying to factor that into my debugging to see if it's the cause, it seems it is. Thoughts?

Yeah I'm aware of that issue, but I've verified in my own projects that it was fixed in RN 0.61.2, so it doesn't seem like it should apply here.

@computerjazz I'm starting to narrow down the cases in which it happens, using fast refresh and saving with the list items on screen is definitely one of them.
~However, there are instances where the renderItem function I pass to the list is called with index undefined - my render item method depends on the index for some logic and I think that might have an effect, any idea why that would happen?~
Just kidding, I dove into the library and read the componentDidUpdate logic. Obviously the nature of CDU isn't synchronous with render, is there something that can be done to ensure index isn't null when it's rendered? Only because it seems the items aren't guaranteed to re-render once the key is set

Oh yeah i've stopped using fast refresh after hitting a few other subtle bugs, which is too bad.
The only reason I can think of that an item wouldn't be called with an index is if it didn't exist in the keyToIndex map, which could be related to the ref issue above. Off the top of my head I'm not sure why that would be happening.

Yeah I had to include lots of performance optimization to make sure list items didn't rerender excessively. I'm sure there's room for improvement in getting items to rerender when we want them to.

I have two proposed solutions:

  1. adding a this.setState after onNextFrame in componentDidUpdate such that the items are at least guaranteed to rerender with the index, and returning null in so that people who rely on the index for rendering logic don't have crashes/unpredictable behavior
  2. doing something in render like
renderItem = () => {
...
if (!this.keyToIndex.get(itemKey)) this.keyToIndex.set(itemKey)
return (
...
)
}

but I don't like doing things with side effects in render

Going to try to the first solution and let you know if I get any weird behavior

@computerjazz not sure why I made it way more complicated than it had to be with the above solutions, I passed an "listIndex" where the index passed to the item is keyToIndex.get(itemKey) || listIndex

been using it in production and haven't had any issues with the pending callbacks

@dereknelson hey I'm running into the same issue. Would you mind explaining your solutions and how to implement? Thanks!

@eric-om when you call the renderItem in props just do something like

{ ...props, index: keyToIndex.get(itemKey) || listIndex }

@dereknelson sorry for the newb question but it's not clear to me where I should be using { ...props, index: keyToIndex.get(itemKey) || listIndex }

I'm passing a renderItem function to the renderItem prop on the DraggableFlatList.

The function: const renderItem = ({ item, index, drag, isActive }) => { ... }

I tried doing this in the renderItem prop renderItem={(props) => renderItem({ ...props, index: keyToIndex.get(itemKey) || listIndex })} but I got error Can't find variable: keyToIndex.

@eric-om when you call the renderItem in props just do something like

{ ...props, index: keyToIndex.get(itemKey) || listIndex }

Hi @dereknelson, could you maybe explain this with a little more context? I am currently running into similar issues and I am not able to completely follow your solution steps - would it be possible to see the entire file / code base - or is it a private app? Anyways - I would really appreciate your help!

Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gazedash picture gazedash  路  5Comments

henriqueweiand picture henriqueweiand  路  5Comments

canpoyrazoglu picture canpoyrazoglu  路  7Comments

ymohdriz picture ymohdriz  路  7Comments

DavidFlores91 picture DavidFlores91  路  10Comments