computerjazz / react-native-draggable-flatlist

A drag-and-drop-enabled FlatList for React Native
MIT License
1.96k stars 413 forks source link

Scroll Bug in NestableScrollContainer #497

Open CharlesZim opened 1 year ago

CharlesZim commented 1 year ago

This package has external dependencies of react-native-reanimated and react-native-gesture-handler which must be installed separately. Before opening an issue related to animations or gestures please verify that you have completed ALL installation steps, including the changes to MainActivity.

Describe the bug When using NestableScrollContainer with react-native-draggable-flatlist, I noticed that if I try to scroll in a different direction while a scroll motion is still ongoing, it works fine. However, if I wait for the scroll to complete and then attempt to scroll again, depending on the gesture, it often fails to scroll.

To Reproduce This issue can be reproduced in the official Snack: "https://snack.expo.dev/@computerjazz/draggable-flatlist-examples". Navigate to the "Nested" Screen. Try scrolling up and, before the scroll completes, scroll down (or vice versa). It works as expected. However, if you wait for the scroll to complete and then attempt to scroll again, you will encounter the issue.

Platform & Dependencies Please list any applicable dependencies in addition to those below (react-navigation etc).

Additional context I have also tried replacing NestableDraggableFlatList with a regular FlatList, and it worked without issues. This leads me to believe that the problem might be specific to NestableDraggableFlatList.

zekicanAYDIN commented 1 year ago

+1

Samad198 commented 12 months ago

I am having the same issue. The frequency of the bug seems do decrease as the value of the activationDistance prop increases

CarterSimonson commented 10 months ago

This seems to be caused by the ScrollView conflicting with the GestureDetector component that DraggableFlatList is using internally. Removing the gesture detector fixes the scroll issue (but breaks everything else obviously)

This issue in the react-native-gesture-handler repo seems relevant. I haven't found a workaround yet but will update here if I do.

CarterSimonson commented 10 months ago

I was able to find a workaround -> https://github.com/CarterSimonson/react-native-draggable-flatlist/pull/1

Here's what I ended up doing:

I initially tried controlling the enabled state from within the drag function but was getting some gnarly race conditions.

This results in the following behavior:

This works but unfortunately required a breaking change to the API.

Demo:

https://github.com/computerjazz/react-native-draggable-flatlist/assets/31598368/479c7be2-ee7c-42cc-8741-851cfcc56608

SethCram commented 3 months ago

Hi @CarterSimonson could you provide some of the code of how you got this working? It doesn't seem your PR fixed this issue by default or I think I'd be seeing my issue resolved unless I need to update react-native-gesture-handler library to the latest. Only solution I've found to this is increasing activationDistance by a gross amount and lowering it when dragging item

CarterSimonson commented 3 months ago

Hey @SethCram,

My goal was to only allow the elements to be dragged when a button on each of them was clicked and dragged: image

Here's a code snippet of the workaround from my component that wraps this drag button on each of my list items. The only change to the way the library is used is that a tapGesture from RN gesture handler is passed as an argument to renderItem. The drag event will not begin unless the tapGesture is fired first, which prevented the scrolling bug in my case.

import { GestureDetector } from 'react-native-gesture-handler';

// Part of the workaround is that a `tapGesture` is now passed as an argument of the renderItem function:
function renderItem({ item, getIndex, tapGesture }: RenderItemParams<KeyedString>) {
  const index = getIndex();

  // The dragging logic will only begin if this tapGesture is fired beforehand
  const right = (
    <GestureDetector gesture={tapGesture}>
      <DragIcon />
    </GestureDetector>
  );

  if (index !== undefined) {
    return (
      <InputListItem
        value={item.value}
        onChangeText={(value) => updateIngredient(value, index)}
        onBlur={() => onBlur(index)}
        right={right}
      />
    );
  }
}
return <NestableDraggableFlatList
  data={ingredients}
  onDragEnd={({ data }) => onChange(data)}
  keyExtractor={(value) => value.id}
  renderItem={renderItem}
/>

One limitation of this is that it doesn't prevent the scrolling bug if the entire list item is a clickable area (like it is in this demo). It takes advantage of the fact that I only needed to support drag events on a small section of each list item.

Hopefully that clears some things up, I am happy to elaborate further if not 😄