atlassian / react-beautiful-dnd

Beautiful and accessible drag and drop for lists with React
https://react-beautiful-dnd.netlify.app
Other
33.29k stars 2.53k forks source link

Add debug helper for missing keys #1673

Open Maxi-Di-Mito opened 4 years ago

Maxi-Di-Mito commented 4 years ago

Hi, I have this repo https://github.com/Maxi-Di-Mito/dnd-test with some dinamic dragable components using HOCs. There a problem with de DnD that throws this error: react-beautiful-dnd Unable to find draggable with id: FU9fcUhJC👷‍ This is a development only message. It will be removed in production builds.

I can DnD consecutive indexes, i mean from 0 to 1, from 2 to 1 etc..., but when i DnD from 2 to 0 it breaks, and then i cant drag some element anymore.

danieldelcore commented 4 years ago

Hi @Maxi-Di-Mito,

Thanks for raising this issue! Can you please create a standalone example on codesandbox.io using our boilerplate: https://codesandbox.io/s/k260nyxq9v Without a standalone example, we will not be able to action this one Cheers!

Maxi-Di-Mito commented 4 years ago

Thank you @danieldelcore . Here it is, i hope it is ok: https://codesandbox.io/embed/vertical-list-zfcrq?fontsize=14&hidenavigation=1&theme=dark

wpmonks commented 4 years ago

I am facing the same issue as described by @Maxi-Di-Mito .

siilike commented 4 years ago

I am also facing this issue.

It seems to be caused by draggableAPI.update. When changing the order it gets run twice. On the first run it removes one of the elements from the entries and overwrites the second one. On the second run it should add back the deleted entry, but it does not, because the unique IDs don't match.

siilike commented 4 years ago

Problem solved: one of the elements was missing the "key" property, so make sure the keys are always consistent.

orgoldfus commented 4 years ago

@siilike can you please elaborate a bit more? what element was missing the "key" property? the Draggable component? If you have a code example it would be very helpful.

siilike commented 4 years ago

The code was something along those lines:

const Element = props => (<Draggable index={props.idx} draggableId={""+props.id}>...</Draggable>)

const Container = props => (
    <Droppable droppableId={this.props.droppableId} isDropDisabled={this.props.disabled}>
        {(provided, snapshot) => (
            <div
                ref={provided.innerRef}
                {...provided.droppableProps}
                className={snapshot.isDraggingOver ? 'dragover' : ''}
            >
                <div>
                    {data.map((a, idx) => <Element idx={idx} {...a} />)}
                </div>

                {provided.placeholder}
            </div>
        )}
    </Droppable>
)

The error is that

{data.map((a, idx) => <Element idx={idx} {...a} />)}

would have to be:

{data.map((a, idx) => <Element key={a.id} idx={idx} {...a} />)}

Otherwise React will reuse the elements upon reordering the items so that it will confuse the draggableAPI update algorithm. The key has to be unique to that element and stay constant.

alexreardon commented 4 years ago

React should warn you if you are doing this. Would it be worth rbd adding a warning as well?

siilike commented 4 years ago

Yes, there is a warning and the key should be set, but it is not immediately obvious that it affects sorting that way and gives an impression that it is a bug. The easiest would be to just add it to the "Unable to find draggable with id" message.

alexreardon commented 4 years ago

I'll see if I can add some more helpful debugging information

alexreardon commented 4 years ago

We cannot really tell if the draggable component is supposed to have a key or not. Technically you can wrap a Draggable in other components. In which case they would need to have the key on it.

So from a draggable we cannot tell if a missing key is correct / incorrect

ignaciolarranaga commented 4 years ago

Side comment guys, I had the same issue because I accidentally passed different things to the draggableId and key. It did work the first element, but later if I try to move the first element, it said it can not found.

Specifically (see key={index} below):

<DragDropContext onDragEnd={this.handleDragEnd}>
  <Droppable droppableId="options">
    {(provided) => (
      <div {...provided.droppableProps} ref={provided.innerRef}>
        { this.state.options.map((option, index) =>
          <Option key={index} option={option} ...

and (see draggableId={this.props.option})

export default class Option extends React.Component<OptionProps, OptionState> {
  render() {
    I18n.setLanguage(this.context.language);
    I18n.putVocabularies(strings);

    return (
      <Draggable draggableId={this.props.option} index={this.props.index}>

The problem gets solved once I adjust the key to match the draggable, i.e.: <Option key={option} Hope it can help.

Frontend-io commented 3 years ago

Side comment guys, I had the same issue because I accidentally passed different things to the draggableId and key. It did work the first element, but later if I try to move the first element, it said it can not found.

Specifically (see key={index} below):

<DragDropContext onDragEnd={this.handleDragEnd}>
  <Droppable droppableId="options">
    {(provided) => (
      <div {...provided.droppableProps} ref={provided.innerRef}>
        { this.state.options.map((option, index) =>
          <Option key={index} option={option} ...

and (see draggableId={this.props.option})

export default class Option extends React.Component<OptionProps, OptionState> {
  render() {
    I18n.setLanguage(this.context.language);
    I18n.putVocabularies(strings);

    return (
      <Draggable draggableId={this.props.option} index={this.props.index}>

The problem gets solved once I adjust the key to match the draggable, i.e.: <Option key={option} Hope it can help.

This solved my issue. The draggableId should match the key of the component Thanks man.

alii13 commented 3 years ago

@siilike thanks man! you saved my 2 hours.

OlegSaidov commented 3 years ago

The code was something along those lines:

const Element = props => (<Draggable index={props.idx} draggableId={""+props.id}>...</Draggable>)

const Container = props => (
  <Droppable droppableId={this.props.droppableId} isDropDisabled={this.props.disabled}>
      {(provided, snapshot) => (
          <div
              ref={provided.innerRef}
              {...provided.droppableProps}
              className={snapshot.isDraggingOver ? 'dragover' : ''}
          >
              <div>
                  {data.map((a, idx) => <Element idx={idx} {...a} />)}
              </div>

              {provided.placeholder}
          </div>
      )}
  </Droppable>
)

The error is that

{data.map((a, idx) => <Element idx={idx} {...a} />)}

would have to be:

{data.map((a, idx) => <Element key={a.id} idx={idx} {...a} />)}

Otherwise React will reuse the elements upon reordering the items so that it will confuse the draggableAPI update algorithm. The key has to be unique to that element and stay constant.

This answer solved the issue for me, just add key prop to your draggable component with the same value as the element id.

martin19 commented 3 years ago

Please solve this. There is no comprehensible reason why "key" and "draggableId" must be the same value. Cost me 2 hours to find out why there are "consecutive key" and "id not found" errors all over the place. While we're at it: I consider the necessity to provide consecutive indexes to the component superfluous - why should this be required for a user to provide? The library should maintain these indexes - at least if they are not provided.

Apart from that, its a very good library and it saved me some days after all.

dikshit-n commented 3 years ago

Problem solved: one of the elements was missing the "key" property, so make sure the keys are always consistent.

This solved to error for me 🎉🎉. Thanks !

Kuldeep-truefan commented 9 months ago

Everything is correct in my code as described above, but it's not working. Then i tried after removing strict mode and it starts working, why it's not working with strict mode?

burakturan commented 3 weeks ago

image

This problem is so serious that it drives people crazy... I failed in all my attempts and stubbornly tried to avoid this mistake, I tried every possible solution but nothing worked.

I use Ant Design Pro in my project. Luckily I finally found this solution: Normally I was passing dragHandleProps to two child components and it was already working, but it was strange that we were still getting the error.

I exported the DragHandleProps to a hidden div in the same directory. It works now and there are no errors.

I'm finally free!