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

DraggableGrid breaks InteractionManager.runAfterInteractions callbacks #83

Open
michalziolkowski opened this issue Jun 8, 2023 · 0 comments

Comments

@michalziolkowski
Copy link

michalziolkowski commented Jun 8, 2023

In our app, we use a lot of InteractionManager.runAfterInteractions callbacks, but the way react-native-draggable-grid is implemented right now can lead to that callback being stuck and not responding at all until the app is restarted.

The problem appears when DraggableGrid gets re-rendered when you are in the middle of dragging an item.
In our specific case, this happens because whenever an item is being dragged we want to change how it looks in that state so we update the component to trigger renderItem with the new state.

Why InteractionManager.runAfterInteractions may break:

During onPanResponderGrant, a PanResponder makes a call to createInteractionHandle which effectively blocks all calls to runAfterInteractions until the handle has been cleared. The value of the handle is stored in the PanResponder's local state, and when PanResponder terminates, if the handle is found to be !== null, it is cleared and the pending tasks are flushed. As discovered in an issue from different library here

Why it breaks forDraggableGrid:

The problem is that when we re-render the DraggableGrid it never terminates properly because PanResponder is initialized with each render so previous instance never terminates.

How this can be solved:

A solution to this would be to initialize PanResponder as it is suggested in react-native docs here, using the useRef, but simply doing that breaks how the component works because we rely on panResponderCapture state and also some props like onDragStart, onDragging, onResetSort and onDragRelease may be updated and this will not be reflected in PanResponder callbacks, because these would be initialized just once with useRef initialization.

Workaround:

As a workaround in our app we've just memoized the DraggableItem and pass data to rendered items through Context so it by-passes the DraggableItem re-rendering, but it feels like a hacky solution. So it should either be documented or there should come some design change to this library so it doesn't break InteractionManager.runAfterInteractions callbacks.

An example code of how we workaround the issue:

CustomGrid.tsx

export const DraggedItemContext = createContext<DraggableItemType | undefined>(
  undefined,
);

const CustomGrid = () => {
  const [currentDraggedItem, setCurrentDraggedItem] = useState();

  const renderItem = (item: DraggableItemType) =>   <CustomDraggableItem key={item.key} item={item} />

   const onDragStart = (item: DraggableItemType) => {
     setCurrentDraggedItem(item);
   };

  const draggableGrid = useMemo(() => {
      return (
        <DraggableGrid
          data={itemsData}
          renderItem={renderItem}
          onDragStart={onDragStart}
        />
      );
    }, [itemsData]);

  return (
      <DraggedItemContext.Provider value={currentDraggedItem}>
        {draggableGrid}
      </DraggedItemContext.Provider>
  };
};

CustomGrid.tsx

const CustomDraggableItem = ({ item }: CustomDraggableItemProps) => {
  const currentDraggedItem = useContext(DraggedItemContext);

  return (
      <CustomGridItem item={item} isBeingDragged={item.key  === currentDraggedItem?.key;} />
  };
};
@michalziolkowski michalziolkowski changed the title InteractionManager.runAfterInteractions breaks because PanResponder is not terminated properly in some cases DraggableGrid breaks InteractionManager.runAfterInteractions callbacks Jun 8, 2023
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

1 participant