React-sortable-hoc: Only First Element Draggable; Uncaught TypeError: Cannot read property 'left' of undefined at _class.animateNodes at _this.handleSortMove

Created on 11 Apr 2018  路  11Comments  路  Source: clauderic/react-sortable-hoc

Hi!
I love how these components work, but now that I'm trying to integrate react-sortable-hoc into my app with my custom components, I'm having issues.

None of the elements in my list are draggable, except for the first one. When I start dragging that element (in fact when I first click it), I see a javascript error in the console:

Uncaught TypeError: Cannot read property 'left' of undefined
    at _class.animateNodes (index.js:593)
    at _this.handleSortMove (index.js:272)

I stepped into the debugger, and discovered that the method _this.getEdgeOffset(node); is returning undefined. I noticed that this function is recursively calling itself many, many times, because node.parentNode !== this.container. The function getEdgeOffset calls itself for every element all the way up to body, html, and document, until it assigns undefined to _this.offsetEdge. I'm not sure if this is the cause, or related in some way, or just a red herring.

Are my components somehow in the wrong order? Is this related to the "Unknown" component I see in the react debugger?

image

Any help would be greatly appreciated.

Here's a bit of my code (too much to paste it all in). Note that <Topic> and <TopicSection> are just presentational components which have several DOM elements wrapped in a parent

element.
```
const SortableTopic = SortableElement((props) => {
return (
{...props}
/>
);
});
const SortableTopicSection = SortableElement((props) => {
return (
{...props}
/>
);
});

const SortableList = SortableContainer(({
items,
onToggleSection,
onChangeSectionName,
onSaveSectionName,
onEditSectionName,
onDeleteSection,
onToggleAllTopics,
onAddNewTopic,
onDragSection,
collapsedSection,
collapsedAllTopics,
editMode,
tempSectionName,
editingSectionName,
collapsedDetails,
onToggleDetails,
collapsedTopic,
onToggleTopic,
onDragTopic,
onDeleteTopic,
}) => {
let lastDivider = '';
return (
items.map( (item, idx) => {
switch( item.type ) {
case 'divider':
// don't render sections that have been deleted
if('deleted' in item && item.deleted === 1) {
return '';
}
lastDivider = item.linkid;
return (
onToggleSection={onToggleSection}
onChangeSectionName={onChangeSectionName}
onSaveSectionName={onSaveSectionName}
onEditSectionName={onEditSectionName}
onDeleteSection={onDeleteSection}
onToggleAllTopics={onToggleAllTopics}
onAddNewTopic={onAddNewTopic}
onDragSection={onDragSection}
collapsedSection={collapsedSection}
collapsedAllTopics={collapsedAllTopics}
editMode={editMode}
tempSectionName={tempSectionName}
editingSectionName={editingSectionName}
section={item}
index={idx}
key={item.linkid}
/>
);

    case 'topic':
      if(!collapsedSection[lastDivider] && !('deleted' in item && item.deleted === 1)) {
        return (
          <SortableTopic
            rootClassName='TopicPlan-topic'
            topic={item}
            collapsedDetails={collapsedDetails[item.uuid]}
            onToggleDetails={onToggleDetails}
            collapsedTopic={collapsedTopic[item.uuid]}
            onToggleTopic={onToggleTopic}
            placement='topicplan'
            editPlanMode={editMode}
            onDragTopicInPlan={onDragTopic}
            onDeleteTopicFromPlan={onDeleteTopic}
            index={idx}
            key={item.uuid}
           />
        );
      } else {
        return '';
      }
    default:
      return (
        <div>Error: unexpected item type: {item.type}</div>
      );
    }
  }
)

...
...
const TopicPlan = ({
...
...


items={items}
onToggleSection={onToggleSection}
onChangeSectionName={onChangeSectionName}
onSaveSectionName={onSaveSectionName}
onEditSectionName={onEditSectionName}
onDeleteSection={onDeleteSection}
onToggleAllTopics={onToggleAllTopics}
onAddNewTopic={onAddNewTopic}
onDragSection={onDragSection}
collapsedSection={collapsedSection}
collapsedAllTopics={collapsedAllTopics}
editMode={editMode}
tempSectionName={tempSectionName}
editingSectionName={editingSectionName}
collapsedDetails={collapsedDetails}
onToggleDetails={onToggleDetails}
collapsedTopic={collapsedTopic}
onToggleTopic={onToggleTopic}
onDragTopic={onDragTopic}
onDeleteTopic={onDeleteTopic}
onSortEnd={({oldIndex, newIndex}) => console.log('onSortEnd called', oldIndex, newIndex)}
/>

Most helpful comment

Update: I discovered the answer to my question. It was necessary to have a DOM element between the <SortableContainer> HOC that gets injected and all of the <SortableItem> HOCs that are within it. I wrapped a <div> tag around the code {items.map((item, idx) => { to resolve the issue.
Great component - thank you @clauderic

All 11 comments

Update: I discovered the answer to my question. It was necessary to have a DOM element between the <SortableContainer> HOC that gets injected and all of the <SortableItem> HOCs that are within it. I wrapped a <div> tag around the code {items.map((item, idx) => { to resolve the issue.
Great component - thank you @clauderic

Damn it! This should go into the docs!

@sconzof You saved my day! 馃憤

I had similar issue. When I've reordered my two images, they reordered, but then stuck and couldn't do anything with it. I've added third image, and then got error from this topic. Nothing worked until I've found out, that the example in docs was causing issues.
The code was:
{items.map((value, index) => ( <SortableItem key={item-${index}} index={index} sortIndex={index} value={value} /> ))}
But if you use index as a key, then when reordered, then these keys are mixed up. So I've changed it to use my own id as a key:
{items.map((item, index) => ( <div id={"item_" + item.id} key={item-${item.id}}>

Now everything works just fine.

Thanks, sconzof! I was returning a React.fragment, changed it to <div> and viola!

@degregar you did my day. Many, many thanks!

@degregar and @sconzof thank you very much!

Got me too :( and wasted about 2 hours of my life :(

@sconzof you just saved me a ton of headache. Thank you!!!!!

I am still facing the issue. I'm trying to update database through API every-time after the reordering the grid. It works well for the first time but after first order it shows error.

Error I am getting :-
Cannot read property 'left' of undefined at WithSortableContainer.animateNodes (react-sortable-hoc.umd.js:1099) at react-sortable-hoc.umd.js:775

Code: -

`const SortableItem = SortableElement( ({elemIndex, value = '', onRemove}) =>
    <GridListTile 
        style={{ height: '150px', width: '250px', display: 'inline-block',  marginRight: '13px'}}
        key={value}>
        <Button 
          type='submit' 
          style={{ position: 'absolute', zIndex: '+1', color: '#EF2D56', marginLeft: '200px'}} 
          onClick={(e) => {onRemove(elemIndex); console.log('Removed', elemIndex, value)}}>
         <Close />
         </Button>
        <img src={value} alt={value}/>
    </GridListTile>
    );

const SortableList = SortableContainer(({ items, onRemove}) => {
  return (
    <ul>
      {items.map((value, i) => ( 
       <div id={'item_'+i} key={`item-${value}-${i}`}>
        <SortableItem 
          key={`item-${value}-${i}`} 
          index={i} 
          elemIndex={i} 
          value={value.imageUrl} 
          onRemove={onRemove}/>
          </div>
        ))}
    </ul>
  );
});
// inside the render method
<SortableList 
            axis='xy'
            items={this.props.images} 
            onSortEnd={this.onSortEnd}
            onRemove={ i => this.onRemove(i)} />

`

This is also happening when the top level dom element has display: contents - I guess there is no way around this...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vxba picture vxba  路  10Comments

slmgc picture slmgc  路  21Comments

smmoosavi picture smmoosavi  路  9Comments

edygar picture edygar  路  10Comments

Miteshdv picture Miteshdv  路  16Comments