1

I am trying to display a list where dragged item is on top. I sorted the list thats nested inside createMemo mapArray. It works in my case, but I couldn't reproduce it with this example.

#1 Problem, why doesnt this example sort and display the dragged items on top. Also createEffect console.log m_atoms_sorted doesn't retrigger even though it touches the .dragging signal of every atom.

#2 Problem, in my case it sorts, but the problem occurs when I try to remove an item from the original array via setList1. In that case list1 correctly updates, but it display's the old items and removes the wrong item.

If I remove the sort the problem dissapears.

import { render } from "solid-js/web";
import { For, createSignal, createMemo, mapArray, createEffect } from "solid-js";

const make_atom = (n) => {
   let [_dragging, _setDragging] = createSignal(false)

   return {
      n,
      get dragging() { return _dragging() },
      set dragging(v: boolean) { _setDragging(v) }
   }
}

function Counter() {
  const [list1, setList1] = createSignal([1, 2, 3, 4, 5]);
  
  const m_atoms = createMemo(mapArray(list1, make_atom))

  const m_atoms_sorted = createMemo(() => {
     return m_atoms()
      .sort((a, b) => a.dragging ? (b.dragging ? 0 : 1) : (b.dragging ? -1 : 0))
  })
  createEffect(() => {
     console.log(m_atoms().map(_ => _.dragging).join(''))
  })
  createEffect(() => {
  console.log(m_atoms_sorted())
  })

  return (<>
    <For each={m_atoms_sorted()}>{ item => 
       <span onClick={_ => item.dragging = !item.dragging}>|{item.n}{item.dragging ? 'drag':'nodrag'}|</span>
    }</For>
    </>)
}

render(() => <Counter />, document.getElementById("app"));

Edit

Ok, If I remove the createMemo for m_atoms_sorted, it correctly puts dragging items on top. So my problem is #2.

eguneys
  • 6,028
  • 7
  • 31
  • 63
  • Updated playground https://playground.solidjs.com/?hash=-358071419&version=1.3.16 – eguneys May 21 '22 at 15:19
  • 1
    Haven't digested your example fully yet. But one thing I'm noticing is that the Array.prototype.sort() method is mutating the original array too. Hence some glitches may arise. It may be better to clone the array before sorting. – thetarnav May 21 '22 at 15:33
  • Ok that fixed it thanks. Btw, when should I start worrying about performance about cloning arrays. – eguneys May 21 '22 at 15:35
  • 1
    I wouldn't worry about that. Not in the application later at least. Cloning is used all of the time in solid as signals really on immutability and it's one of the most performant frameworks out there. Has to create new array too and you don't even notice. The sorting method itself is much more costly. – thetarnav May 21 '22 at 15:46
  • Would either of you mind adding the correct answer so we can get an "accepted" answer on this? – Nick May 21 '22 at 23:54

1 Answers1

1

The component logic in your example is not clear enough but mapArray and indexArray does the memoization internally, so you don't have to do it in advance.

You can reorder elements more easily if you store them in a signal and update the order accordingly.

// Swap the elements in place and return a new array
// otherwise signal will not update.
function swapElements(arr: Array<any>, a: number, b: number) {
  let temp = arr[a];
  arr[a] = arr[b];
  arr[b] = temp;
  return [...arr];
};

const initialVal = Array.from({ length: 10 }, ((v, i) => `Item #${i}`));

const App = () => {
  const [items, setItems] = createSignal(initialVal);
  const [prevPos, setPrevPos] = createSignal(0);

  const handleStart = (event: MouseEvent) => {
    setPrevPos(event.target.dataset.order);
  };

  const handleDrop = (event: MouseEvent) => {
    event.preventDefault();
    const nextPos = event.target.dataset.order;
    const arr = swapElements(items(), nextPos, prevPos());
    setItems(arr);
  };

  const handleDragOver = (event: MouseEvent) => {
    event.preventDefault();
  };


  return (
    <ul>
      {mapArray(items, (item, index) => {
        return (
          <li
            ondragstart={handleStart}
            ondragover={handleDragOver}
            onDrop={handleDrop}
            data-order={index()}
            draggable={true}
          >
            {item}
          </li>
        );
      })}
    </ul>
  );
}
snnsnn
  • 10,486
  • 4
  • 39
  • 44