react-beautiful-dnd icon indicating copy to clipboard operation
react-beautiful-dnd copied to clipboard

`Non-consecutive index` error when using redux thunk even with array index passed to droppable index

Open rolandjohann opened this issue 4 years ago • 14 comments

I have a droppable list whose list items can be reordered. onDragEnd dispatches a redux thunk action that calls the api and updates the state which gets passed to the component that renders the droppable list. DragDropContext is outside of the component. App will be served via nextjs with server side rendering and resetServerContext() before rendering each page server side.

Expected behavior

On drop the animation takes place, onDragEnd dispatches the action causing the api call, because of the response delay the UI will glitch (no reordering will be rendered), the response returns and the list renders in the correct order.

After that item reordering should still be possible without any glitches, errors, etc.

Actual behavior

After first movement, reordering of the previously moved item is not possible and the console prints

Unable to find draggable with id: {"type":"FoodPlanPosition","foodPlanPositionId":12}

When dragging another item the UI is simply broken and the console prints

Detected non-consecutive <Draggable /> indexes.(This can cause unexpected bugs)0, [🔥2], 3, 4, 5

The strange thing is that the list items droppable index will be taken from array index which can't get non-consecutive:

                  {props.baseFoodPlanPositions.map((position, idx) => {
                    return (
                      <Draggable
                        key={idx}
                        index={idx}
                        draggableId={buildFoodPlanPositionDraggableId(position.id)}
                      >
                        {(provided, _snapshot) => {
                          return (
                            <ListItem
                              ref={provided.innerRef}
                              {...provided.draggableProps}
                              {...provided.dragHandleProps}
                              button
                              className={classes.dailyFoodPlanPosition}
                            >
                               {position.foodPlanComponent.name}
                            </ListItem>
                          )
                        }}
                      </Draggable>
                    )
                  })}

Even react component inspector and heavy debugging revealed that the dnd draggable and droppable gets their right id and index. Draggable and Droppable ids are unique (contain database primary key).

In addition I observed strange behavior when adding droppables too fast, whose surrounding components gets rendered via the lifecycle depicted above (click, dispatch, API call, response, stateChange, render). When Items from other lists will be dropped there, they get the wrong droppable ID and therefore will be appended to the wrong list.

This seems to be some kind of race condition and hope anyone can provide a hint where to start. I tried to rebuild the scenario (redux managed list) via code sandbox, but can't emulate API calls and nextjs there.

Steps to reproduce

IMHO too complex to provide currently - If the details provided above are too vague and analysis of the problem is not possible, I'll break things down and provide one.

Current one that uses redux, but no API calls and nextjs, and has no unexpected behavior: https://codesandbox.io/s/react-redux-material-ui-react-beautiful-dnd-bug-itp55

What version of React are you using?

[email protected]:
  version "16.9.0"

What version of react-beautiful-dnd are you running?

react-beautiful-dnd@^13.0.0:
  version "13.0.0"

What browser are you using?

latest Chrome

Demo

rolandjohann avatar Mar 02 '20 23:03 rolandjohann

I used the array index as ReactElement key, which lead to the error.

Is it possible to move the hint https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/api/draggable.md#keys-for-a-list-of-draggable- into the section required-props? After reaching optional pros I just scanned over the site and missed the point of key. Even better to point to the equal important key at the index rule and even at setup problems.

rolandjohann avatar Mar 03 '20 16:03 rolandjohann

I'm running into the same problem. Did you find a way out by any chance? Thanks! @rolandjohann

Tinyik avatar May 04 '20 20:05 Tinyik

Even I am running into same problem. Any way out for this issue?

TejaReddy7 avatar Jun 29 '20 11:06 TejaReddy7

facing same issue, is fix expected? when?

dasmeer avatar Jun 29 '20 11:06 dasmeer

I am running into the same issue. Has there been a fix for it?

KarlaLGA avatar Jul 13 '20 21:07 KarlaLGA

I'm also facing this issue. Any solution?

victorpinheiro avatar Jul 20 '20 12:07 victorpinheiro

The OP mentions the solution (see the doc in the 2nd comment) Moreover, I think the issue is that the tutorial on egghead doesn't specify key for a draggable either - which can potentially lead to a lot of users facing the issue

yogeshnachnani avatar Jul 28 '20 05:07 yogeshnachnani

The OP mentions the solution (see the doc in the 2nd comment) Moreover, I think the issue is that the tutorial on egghead doesn't specify key for a draggable either - which can potentially lead to a lot of users facing the issue

In the egghead tutorial, the Draggable component is inside the child list component

image

but in the code shown in this issue, the Draggable component is wrapping the child list component.

image

In order to add the key property on the Draggable component, is it better to have the code structure as in the first or second scenario? @yogeshnachnani

KarlaLGA avatar Jul 28 '20 20:07 KarlaLGA

I was following the tutorial and bump into the same problem.

I just add the key property to my Draggable component and everything start to work as expected.

change from this

<Draggable draggableId={id} index={index}>
            {(provided) => (
                <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
                    <Tile>
                        <p>{id}</p>
                    </Tile>
                </div>
            )}
        </Draggable>

to this

<Draggable key={id} draggableId={id} index={index}>
            {(provided) => (
                <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
                    <Tile>
                        <p>{id}</p>
                    </Tile>
                </div>
            )}
        </Draggable>

pierluigigiancola avatar Jul 29 '20 02:07 pierluigigiancola

Change this:

<Draggable draggableId={id} index={index}>
            {(provided) => (
                <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
                    <Tile>
                        <p>{id}</p>
                    </Tile>
                </div>
            )}
        </Draggable>

to

<Draggable draggableId={id}  key={id} index={index}>
            {(provided) => (
                <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
                    <Tile>
                        <p>{id}</p>
                    </Tile>
                </div>
            )}
        </Draggable>

Don't use index as the key

spmsupun avatar Aug 07 '20 19:08 spmsupun

Experienced the same problem. Adding item.id as a key to the <Draggable> helped (index did not)

Thanks @spmsupun for pointing out to do not use index as key.

millerox avatar Oct 07 '20 23:10 millerox

Change this:

<Draggable draggableId={id} index={index}>
            {(provided) => (
                <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
                    <Tile>
                        <p>{id}</p>
                    </Tile>
                </div>
            )}
        </Draggable>

to

<Draggable draggableId={id}  key={id} index={index}>
            {(provided) => (
                <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
                    <Tile>
                        <p>{id}</p>
                    </Tile>
                </div>
            )}
        </Draggable>

Don't use index as the key

In my case the problem was in the draggableId, I was using non-unique field :) Just in case someone stumbles into this as well

PabloDinella avatar Apr 20 '21 21:04 PabloDinella

@pierluigi-giancola Thanks a lot 🚀❤️

knowankit avatar Jun 06 '21 17:06 knowankit

I was following the tutorial and bump into the same problem.

I just add the key property to my Draggable component and everything start to work as expected.

change from this

<Draggable draggableId={id} index={index}>
            {(provided) => (
                <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
                    <Tile>
                        <p>{id}</p>
                    </Tile>
                </div>
            )}
        </Draggable>

to this

<Draggable key={id} draggableId={id} index={index}>
            {(provided) => (
                <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
                    <Tile>
                        <p>{id}</p>
                    </Tile>
                </div>
            )}
        </Draggable>

it's solved my problem, thanks a lot.

mojeeto avatar Sep 11 '22 14:09 mojeeto