Skip to content

[Bug]: Empty space / missing item after adding or replacing data; fixed by starting a drag #565

@julian-gargicevich

Description

@julian-gargicevich

Description

When the data of a Sortable.Grid changes in a way that is not a reorder — appending a new item (add), or replacing an item in place while keeping the same keyExtractor key — the grid's internal layout sometimes goes stale:

  • an empty gap is left where a row should be, or
  • the replaced item renders blank / disappears.

The grid never re-measures the affected row on its own. The bug fixes itself the instant a drag gesture starts — beginning a drag forces a re-measure/relayout and the gap or missing item immediately snaps back into place.

So it appears that structural data changes that don't change item order don't reliably trigger a re-measure of the affected row, and the drag handler is what forces it.

This looks closely related to the (closed) #286 — "New items are sometimes placed in the wrong position" / "the last item added is invisible".

Expected behavior

After adding or replacing an item, the grid should lay out the new/changed row correctly without requiring a drag gesture to trigger a relayout.

Notes / workaround

Keeping a stable keyExtractor key across reorders is intentional in our app (so a slot keeps its drag identity when its content is replaced). Our current workaround is to bump a React key on the Sortable.Grid itself — forcing a full remount — only when an add/replace happens (detected via an order-independent signature of key → itemId), and not on reorder. A remount produces a correct layout, which further suggests the issue is a missed internal re-measure rather than bad data.

Steps to reproduce

  1. Render a single-column Sortable.Grid with a keyExtractor returning a stable per-item id.
  2. From a button (outside the grid), append a new item to data — or replace an existing item with a new one while returning the same keyExtractor key for that slot.
  3. Observe (intermittently) an empty gap where the new/changed row should be, or the replaced row rendered blank.
  4. Start dragging any row — the layout immediately corrects itself.

Code snippet, Snack or GitHub repository link

We have not yet isolated this in the example app (the same difficulty noted in #286 — it reproduces in our app but is intermittent). The grid is used roughly like this:

const keyExtractor = useCallback(
  (item: Item) => item.uid, // stable per-slot id; survives replace
  []
);

<Sortable.Grid
  columns={1}
  data={items}
  dimensionsAnimationType="layout"
  keyExtractor={keyExtractor}
  scrollableRef={scrollRef}
  rowGap={0}
  columnGap={0}
  onDragEnd={({ data }) => setItems(data)}
  renderItem={({ item }) => (
    <Animated.View
      layout={LinearTransition.duration(220)}
      exiting={FadeOut.duration(160)}
    >
      <Row item={item} />
    </Animated.View>
  )}
/>

// elsewhere:
// add:     setItems(prev => [...prev, newItem])
// replace: setItems(prev => prev.map(i => i.uid === target.uid ? { ...replacement, uid: i.uid } : i))

I'm happy to put together a standalone repro if it would help — please let me know.

React Native Sortables version

1.9.4

Reanimated version

4.3.1

React Native Gesture Handler version

2.31.2

React Native version

0.85.3

Platforms

iOS (confirmed via screen recording). Suspected on Android as well — #286 reported the "invisible last item" specifically on Android.

Additional information

  • React: 19.2.3
  • Expo SDK: 56
  • Architecture: Fabric (New Architecture)
  • Workflow: Expo Dev Client
  • dimensionsAnimationType="layout", columns={1}, rowGap={0}, columnGap={0}, rows wrapped in an Animated.View with Reanimated LinearTransition layout + FadeOut exiting animations.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions