如何在ReactJS中通过TouchStart实现移动端表格行位置交换?
React移动端表格行触摸交换实现方案
问题根源分析
你遇到的核心问题是状态管理逻辑有误:
- 初始状态用空对象
{},导致首次触摸时origin !== undefined始终为真,触发错误的交换逻辑; - 交换后未正确重置源状态,导致后续每次触摸都直接触发交换;
- 直接比较对象引用不可靠,React中数据更新后对象引用会变化,容易出现判断失误。
解决方案代码实现
我们通过跟踪源行唯一ID而非整个对象、明确区分“选源”和“选目标”状态来解决问题:
import { useState } from 'react'; const TableWithTouchSwap = () => { // 模拟表格数据,每个项必须有唯一ID const [items, setItems] = useState([ { id: 1, name: 'Object 1' }, { id: 2, name: 'Object 2' }, { id: 3, name: 'Object 3' } ]); // 存储选中的源行ID,初始为null表示未选源 const [originId, setOriginId] = useState(null); // 交换两个位置的项,使用函数式更新确保拿到最新状态 const swapItems = (sourceId, targetId) => { setItems(prevItems => { const sourceIdx = prevItems.findIndex(item => item.id === sourceId); const targetIdx = prevItems.findIndex(item => item.id === targetId); if (sourceIdx === -1 || targetIdx === -1) return prevItems; const newItems = [...prevItems]; [newItems[sourceIdx], newItems[targetIdx]] = [newItems[targetIdx], newItems[sourceIdx]]; return newItems; }); }; const handleTouchStart = (e, rowObj) => { e.preventDefault(); // 阻止浏览器默认触摸行为(如滚动) const currentId = rowObj.id; if (originId) { if (originId !== currentId) { // 第二次触摸不同行:执行交换并重置源状态 swapItems(originId, currentId); setOriginId(null); } else { // 触摸同一行:取消选中 setOriginId(null); } } else { // 第一次触摸:设置源行 setOriginId(currentId); } }; return ( <table> <thead> <tr><th>Rank</th></tr> </thead> <tbody> {items.map(item => ( <tr key={item.id} onTouchStart={(e) => handleTouchStart(e, item)} // 可选:高亮选中的源行,提升用户体验 style={{ backgroundColor: originId === item.id ? '#f0f0f0' : 'transparent', cursor: originId ? 'pointer' : 'default' }} > <td>{item.name}</td> </tr> ))} </tbody> </table> ); }; export default TableWithTouchSwap;
关键改进点
- 用唯一ID跟踪状态:避免直接比较对象引用的不可靠性,确保状态判断准确;
- 明确状态流转:
- 无选中状态时,触摸行设置为源;
- 有选中状态时,触摸不同行触发交换并重置状态;
- 触摸同一行取消选中;
- 函数式更新状态:确保
swapItems中拿到最新的items数组,避免异步状态更新导致的错误; - 阻止默认触摸行为:添加
e.preventDefault()避免滚动等干扰,提升触摸操作的稳定性。
为什么之前重置origin会失效?
你之前尝试交换后重置origin为undefined但失效,是因为:
- 初始状态用了空对象
{},origin !== undefined始终为真,首次触摸就进入交换逻辑; - React的
useState是异步更新的,直接在交换后同步重置状态可能导致逻辑时序混乱,改用函数式更新和ID跟踪后就能解决这个问题。
内容的提问来源于stack exchange,提问作者user12299392
相关产品推荐
相关产品推荐

