You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ReactJS中通过TouchStart实现移动端表格行位置交换?

React移动端表格行触摸交换实现方案

问题根源分析

你遇到的核心问题是状态管理逻辑有误:

  1. 初始状态用空对象{},导致首次触摸时origin !== undefined始终为真,触发错误的交换逻辑;
  2. 交换后未正确重置源状态,导致后续每次触摸都直接触发交换;
  3. 直接比较对象引用不可靠,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;

关键改进点

  1. 用唯一ID跟踪状态:避免直接比较对象引用的不可靠性,确保状态判断准确;
  2. 明确状态流转:
    • 无选中状态时,触摸行设置为源;
    • 有选中状态时,触摸不同行触发交换并重置状态;
    • 触摸同一行取消选中;
  3. 函数式更新状态:确保swapItems中拿到最新的items数组,避免异步状态更新导致的错误;
  4. 阻止默认触摸行为:添加e.preventDefault()避免滚动等干扰,提升触摸操作的稳定性。

为什么之前重置origin会失效?

你之前尝试交换后重置origin为undefined但失效,是因为:

  • 初始状态用了空对象{},origin !== undefined始终为真,首次触摸就进入交换逻辑;
  • React的useState是异步更新的,直接在交换后同步重置状态可能导致逻辑时序混乱,改用函数式更新和ID跟踪后就能解决这个问题。

内容的提问来源于stack exchange,提问作者user12299392

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 06:16:05