I'm fairly new to react. I currently have a project that's working as intended on desktop, and I am attempting to add mobile functionality. I am displaying a bunch of objects in a table, and I want the user to be able to switch the positions of the objects in the table using touch on mobile.
When a user touches a row, we can say that's the origin position. The next consecutive time they touch a row, that is the destination position. I want it to work so then, it would swap origin with destination upon touching the destination position.
For example I have a table:
<table>
<thead>
<tr><th>Rank</th></tr>
</thead>
<tbody>
<tr onTouchStart={(e) => handleTouchStart(e, rowObj)}}><td>Object 1</td></tr>
<tr onTouchStart={(e) => handleTouchStart(e, rowObj)}}><td>Object 2</td></tr>
<tr onTouchStart={(e) => handleTouchStart(e, rowObj)}}><td>Object 3</td></tr>
</tbody>
</table>
I want the user to be able to swap the positions of object 1 and object 2 on the table.
This intended functionality is currently working with draggable, onDragStart and onDragEnd on most devices but it doesn't work with mobile. I suspect it has something to do with what is described here:
Is the react-draggable not support on mobile platform?
I wasn't able to figure anything out from the above solution.
Unfortunately, onTouchStart() and onTouchEnd() work different than onDragStart() and onDragEnd(). The touch functions both handle the same object and do not care about where you end your touch. They only care about what object you originally touched. Whereas the drag functions it is easier because dragStart interacts with the original click and dragEnd interacts with where your cursor ends (so, you just swap them).
I figured I could just use only onTouchStart, since onTouchEnd doesn't help, for the functionality already described above (second paragraph). I have it almost working but I am running into issues with useState().
I am setting the first row to a useState on the first touch start, lets say:
const (origin, setOrigin) = useState({})
const handleTouchStart = (e, rowObj) =>
{
setOrigin(rowObj)
}
Then, I am swapping it with the rowObj upon the second touch in handleTouchStart where:
const handleTouchStart = (e, rowObj) =>
{
if (origin !== undefined)
{
swap(origin, rowObj)
}
setOrigin(rowObj)
}
This works the first time. The issue is that after the swap, the useState still remembers an origin. So, the next time a row is touched, it does a swap, and repeats that behavior.
So instead of it being: touch, touch, swap -> touch, touch, swap -> touch, touch, swap
reality: touch, touch, swap -> touch, swap -> touch, swap
I tried setting origin back to undefined after the swap, and it didn't work. When I try that, the swap itself doesn't even occur. Additionally, you can't just have two useStates and swap them, because of the way ontouchStart() works. ontouchStart() would override both useStates.
I figured it out. Seems a bit janky and possibly there exists a better, more clean, more optimal solution. However, I now have it working as intended by doing:
Create a useState that holds whether a swap happened the last state:
const [justSwapped, setJustSwapped] = useState(false)
Then, we work inside of the function
const handleTouchStart = (e, obj) =>
First, create a check that returns if the origin === obj. This means, that the user touched the same row twice (or many times). Theoretically, you could swap it with itself, but that doesn't seem necessary and overly-complicated.
if (origin === obj)
{
return
}
Next, create another case to check if the previous state was a swapping state, AND to check if the origin is undefined. The first condition is necessary to prevent undesirable swapping immediately after a swap, and the second condition is necessary to prevent swapping with undefined on the initial touch.
else if ((!justSwapped) && (origin.first !== undefined))
{
swap(origin, obj)
setOrigin({}) //reset origin
setJustSwapped(true)
return
}
Finally, the end of the function:
else
{
setOrigin(obj)
setJustSwapped(false)
}
Not sure if this will help anybody but I figured I may as well share it.