TahaSh / swapy

✨ A framework-agnostic tool that converts any layout into a drag-to-swap one with just a few lines of code https://swapy.tahazsh.com/
MIT License
5.86k stars 114 forks source link

Layout Breaks After Updating State in Swapy #70

Open tommdq opened 3 weeks ago

tommdq commented 3 weeks ago

Hey there,

I'm trying to implement Swapy in React. I want to be able to update the index of the current element swapped. However, the Swapy layout breaks after updating the state (which contains the key).

I found there's a swapy.setData function but couldn't find any documentation about it. Is what I'm explaining possible to accomplish?

The function I'm calling looks something like this. It works perfectly as it updates the state by exchanging the index, but Swapy doesn't seem to like it, and the layout breaks:

const updateCommunications = (swapyArray) => {
    // Array with itemIds in the new order after dragging [2, 1, 3] for example
    const newOrder = swapyArray.map(({ itemId }) => parseInt(itemId, 10));
    const reorderedCommunications = newOrder.map((id, index) => {
        // Find the communication with the matching id and return it with the new id and the starter index
        const communication = communications.find((comm) => comm.id === id);
        return { ...communication, id: index + 1 };
    });
    setCommunications(reorderedCommunications);
};

For the Swapy setup, I'm mapping over the communications array, using index + 1 as the slot and the communication.id as data-swapy-item. This works perfectly, but when trying to update the state:

swapy.onSwap((event) => {
    const { array } = event.data;
    updateCommunications(array);
});

The drag and drop functionality stops working.

Any guidance on how to resolve this issue would be greatly appreciated!

Thank you!

TahaSh commented 2 weeks ago

@tommdq your case requires using manualSwap. Here's a code example in react. I've also explained how it works in this video tutorial. Thanks!

tommdq commented 1 week ago

@tommdq your case requires using manualSwap. Here's a code example in react. I've also explained how it works in this video tutorial. Thanks!

Thanks so much! This is helping a lot.. But I'm still having some issues to make it work.

The problem occurs when I populate the items array ( in my code called communications ) with data from an API. The slotItemMap state gets updated correctly initially, but then it gets overwritten, leaving only one item and an empty slot.

This is a little code snippet

    const [slotItemMap, setSlotItemMap] = useState<SlotItemMap>([
    { slotId: `${Math.round(Math.random() * 99999)}`, itemId: null }
    ])

    useEffect(() => {
        if (communications.length) {
            const newSlotItemsMap = communications.map((item) => ({
                slotId: item.id.toString(),
                itemId: item.id.toString()
            }))
            setSlotItemMap(newSlotItemsMap)
        }
    }, [communications])

    const slottedItems = useMemo(
        () =>
            slotItemMap.map(({ slotId, itemId }) => ({
                slotId,
                itemId,
                communication: communications.find((comm) => comm.id.toString() === itemId)
            })),
        [communications, slotItemMap]
    )
    console.log('slottedItems', slottedItems)

    useEffect(() => {
        const container = document.querySelector('.swapy-container')!
        swapyRef.current = createSwapy(container, {
            manualSwap: true
        })

        swapyRef.current.onSwap(({ data }) => {
            // You need to call setData because it's a manualSwap instance
            console.log('data', data.array)
            swapyRef.current?.setData({ array: data.array })
            setSlotItemMap(data.array)
        })

        return () => {
            swapyRef.current?.destroy()
        }
    }, [])

Observations When using mock data without the useEffect that waits for communications.length, the drag-and-drop functionality works perfectly. The issue seems to be related to how the slotItemMap state is initialized and updated.

Hope this helps !

I'm using swapy 0.3.1 version.

tommdq commented 1 week ago

I managed to get it working but now I'm getting another issue respecting listeners after using manual swap a few times. I'm following this example and the listeners go crazy after a few swaps.

Is this same issue but with the manual swap

The listeners being overcharged are mainly _resize and _scroll__

Before: image

After a few d&drops: image

This of course it's causing a lot of performance issues and gets very laggy

TahaSh commented 1 week ago

@tommdq Thanks for reporting this. I'll debug this soon and let you know.

TahaSh commented 1 week ago

@tommdq Cleaning up event listeners should now be fixed in v0.4.2. Let me know if you still have any issues.

tommdq commented 1 week ago

@tommdq Cleaning up event listeners should now be fixed in v0.4.2. Let me know if you still have any issues.

Thank you so much for looking into this!

I just tested v0.4.2, and I can confirm that the event listeners are now being cleaned up perfectly. However, I'm still experiencing significant performance issues. This time, the problem seems to be related to the callback function associated with requestAnimationFrame.

I've tried disabling animations when initializing Swapy, but the performance loss persists. Here's a screenshot of the warning in the console: image

Additionally, here’s a short video showing the issue in action:

https://github.com/user-attachments/assets/fc7fa376-db01-4824-8f02-07252a3271ae