Search code examples
javascriptreactjssortablejs

react-sortablejs - Setting the 'onChange' method on an object with nested arrays


I'm using the react-sortablejs library. When trying to move cards within the list. I get the error:

Cannot read property 'map' of undefined

I have a dense structure and it gets lost here. How to handle onChange so that I can see in the console that the order of the notes within the list has changed.

Demo here

import Sortable from 'react-sortablejs';

// Functional Component
const SortableList = ({ items, onChange }) => {  
    return (
        <div>
          <Sortable
            tag="ul"
            onChange={(order, sortable, evt) => {
              console.log(order)
              onChange(order);
            }}

          >
            {items.listItems.map(val => {
             return <li key={uniqueId()} data-id={val}>List Item:  {val.title}</li>})
            }
          </Sortable>
        </div>
    );
};

class App extends React.Component {
    state = {
       item: {
        id: "abc123",
        name: "AAA",
        lists: [
          {
            id: "def456", 
            list_id: "654wer",
            title: 'List1',
            desc: "description",
            listItems: [
              {
                id: "ghj678", 
                title: "ListItems1",
                listItemsId: "88abf1"
              },
              {
                id: "poi098", 
                title: "ListItems2",
                listItemsId: "2a49f25"
              },
              {
                id: "1oiwewedf098", 
                title: "ListItems3",
                listItemsId: "1a49f25dsd8"
              }
            ]   
          },
          {
            id: "1ef456", 
            list_id: "654wer",
            title: 'List 2',
            desc: "description",
            listItems: [
              {
                id: "1hj678", 
                title: "ListItems4",
                listItemsId: "18abf1"
              },
              {
                id: "1oi098", 
                title: "ListItems5",
                listItemsId: "1a49f25"
              },
              {
                id: "1oiwewe098", 
                title: "ListItems6",
                listItemsId: "1a49f25dsd"
              }
            ]   
          },
          {
            id: "2ef456", 
            title: 'List 3',
            list_id: "254wer",
            desc: "description",
            listItems: [
              {
                id: "2hj678", 
                title: "ListItems7",
                listItemsId: "28abf1"
              },
              {
                id: "2oi098", 
                title: "ListItems8",
                listItemsId: "234a49f25"
              },
              {
                id: "df098", 
                title: "ListItems9",
                listItemsId: "1asd8"
              }
            ]   
          }
        ]
      }
    };

    render() {
      const c = this.state.item['lists'].map(item => { return item.listItems});

        return (
          this.state.item['lists'].map(item => {       
            return  (<div>
                      {item.title}
                      <SortableList
                        key={uniqueId()}
                        items={item}
                        onChange={(item) => {
                          console.log(item)
                          this.setState({item});
                        }}
                      >
                      </SortableList>
                    </div>)
          })
        )
    }
};

Thanks in advance.


Solution

  • You have to update few changes in your code.

    Update the SortableList function as below.

    First pass data-id={val.id} in li and after that in onChange method you will receive the order with id. So based on that we are sorting the records.

    const SortableList = ({ items, onChange }) => {  
        return (
            <div>
              <Sortable
                tag="ul"
                onChange={(order, sortable, evt) => {
                  items.listItems.sort(function(a, b){
                      return order.indexOf(a.id) - order.indexOf(b.id);
                  });
                  onChange(items);
                }}
              >
                {items.listItems.map(val => {
                 return <li key={uniqueId()} data-id={val.id}>List Item:  {val.title}</li>})
                }
              </Sortable>
            </div>
        );
    };
    

    Update the onChange event of App component.

    onChange={(item) => {
      let itemObj = {...this.state.item};
      itemObj.lists.map(x=>{
        if(x.id === item.id) x = item;
      });
      this.setState({itemObj});
    }}
    

    That's it!

    Here is the working demo for you
    https://stackblitz.com/edit/react-sortablejs-blzxwd