handsome
handsome

Reputation: 2412

drag to reorder and save state with reactjs

I'm writing a small app that uses the react-sortable-hoc

everything is great but im having issues displaying the list ordered by order

I have

user 0
user 1
user 2

when I drag user 2 above user 0 instead of getting

user 2
user 0
user 1

I get

user 2
user 1
user 0

I think It has to do with the way I'm setting the order in the state. but I can't figure it out.

this is how I set the order on sort end

const onSortEnd = ({ oldIndex, newIndex }) => {

    setUsers(prevState => {
        const newItems = [...prevState];
        newItems[newIndex].order = oldIndex;
        newItems[oldIndex].order = newIndex;
        return newItems.sort((a, b) => a.order - b.order);
    })
};

here's the app running so you can play with it. https://codesandbox.io/s/winter-https-xelrd?fontsize=14&hidenavigation=1&theme=dark

Upvotes: 1

Views: 2689

Answers (3)

Gabriele Petrioli
Gabriele Petrioli

Reputation: 195992

What you do is swapping.

If you want to just "insert" the element in the new position you will have to update all the items between the two positions.

In your case, one approach would be to just move the element and re-create the order for all items

setUsers(prevState => {
  const newItems = [...prevState];
  newItems.splice(newIndex, 0, newItems.splice(oldIndex, 1)[0]).forEach((item,index)=>{
    item.order = index;
  });

  return newItems
});

Demo at https://codesandbox.io/s/confident-river-mrh3p

Upvotes: 1

DannyMoshe
DannyMoshe

Reputation: 6255

So looks like your code is simply swapping the elements. This does not seem like what you really want to do. In fact you really want to remove the element and insert it at a given position. I think since you already have the oldIndex and newIndex, you can approach the sort function as follows:

  const onSortEnd = ({ oldIndex, newIndex }) => {
    setUsers(prevState => {
      var newItems = [...prevState];
      let elem = newItems[oldIndex]
      newItems.splice(oldIndex, 1)
      newItems.splice(newIndex, 0, elem)

     return newItems 
    });
  };

There isn't really a need for order and is capturing more than the minimum state required (unless you use it elsewhere).

Upvotes: 0

Suman Barick
Suman Barick

Reputation: 3411

I have fixed it, here is the working url to play with https://codesandbox.io/s/quizzical-colden-rm62y

You were correct in guessing that the problem was with the onSortEnd function. Instead of swapping the newIndex and oldIndex position we just need to either bubble them up or down.

Here is a working code, it can be cleaned up a bit, but you got the idea :)

const onSortEnd = ({ oldIndex, newIndex }) => {
    setUsers(prevState => {
      const newItems = [...prevState];

      if (oldIndex > newIndex) {
        for (let i = oldIndex - 1; i >= newIndex; i--) {
          newItems[i].order++;
          newItems[oldIndex].order = newIndex;
        }
      } else if (oldIndex < newIndex) {
        for (let i = oldIndex + 1; i <= newIndex; i++) {
          newItems[i].order--;
          newItems[oldIndex].order = newIndex;
        }
      }
      return newItems.sort((a, b) => a.order - b.order);
    });
  };

Hope it helps. Happy coding :)

Upvotes: 1

Related Questions