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

React表格点击表头升降序排序功能失效问题求助

React 表格列排序无响应问题排查与修复

问题描述

我在React Web应用中实现表头点击排序功能:首次点击指定列表头时,该列API数据按升序排序;再次点击则按降序排序。但点击任意表头均无反应,既不执行排序也没有控制台报错。以下是Table.tsx组件中的实现代码片段:

export const MainTable = ({ data, isLoading, error }: MainTableProps) => {

 ...

  // Sorting logic
  const rawData = data.map((group) => group.equipments).flat();
  const [tableData, setTableData] = useState(rawData);
  const [order, setOrder] = useState("ASC");

  const handleHeaderClick = (col) => {
    if (order === "ASC") {
      const sorted = [...tableData].sort((a, b) =>
        a[col].toLowerCase() > b[col].toLowerCase() ? 1 : -1
      );
      setTableData(sorted);
      setOrder("DSC");
    }
    if (order === "DSC") {
      const sorted = [...tableData].sort((a, b) =>
        a[col].toLowerCase() < b[col].toLowerCase() ? 1 : -1
      );
      setTableData(sorted);
      setOrder("ASC");
    }
  };

  ...

  return (
    <>
      <TableContainer>
        <Table variant="simple">
          <Thead>
            <Tr>
              <Th onClick={() => handleHeaderClick("manufacturer")}>
                {t("manu")}
              </Th>
              <Th onClick={() => handleHeaderClick("model")}>{t("model")}</Th>
              <Th
                onClick={() => handleHeaderClick("product-family-display-name")}
              >
                {t("desc")}
              </Th>
              <Th onClick={() => handleHeaderClick("serial-number")}>
                {t("serial")}
              </Th>
              <Th onClick={() => handleHeaderClick("state")}>{t("prov")}</Th>
              <Th onClick={() => handleHeaderClick("city")}>{t("city")}</Th>
              <Th
                onClick={() => handleHeaderClick("regular-price")}
                className="align-right"
              >
                {t("regPrice")}
              </Th>
              <Th
                onClick={() => handleHeaderClick("price")}
                className="align-right sale-header"
              >
                {t("salePrice")}
              </Th>
            </Tr>
          </Thead>
          <Tbody>
            {currentPageData.map((equipment: Equipment) => (
              <Tr key={equipment.id}>
                <Td>
                  {equipment.manufacturer.length > 0
                    ? equipment.manufacturer
                    : "N/A"}
                </Td>
                <Td>{equipment.model.length > 1 ? equipment.model : "N/A"}</Td>
                <Td>{equipment["product-family-display-name"]}</Td>
                <Td>{equipment["serial-number"]}</Td>
                <Td>{equipment.state}</Td>
                <Td>
                  <a
                    target="_blank"
                    href={`https://website.com/locations/${
                      equipment.city.indexOf(" ")
                        ? equipment.city.replace(/\s/g, "-")
                        : equipment.city
                    }`}
                  >
                    {equipment.city}
                  </a>
                </Td>
                <Td className="align-right reg-price">
                  {formatPrice(Number(equipment["regular-price"]?.text))}
                </Td>
                <Td className="align-right sale-price">
                  <strong>
                    {formatPrice(Number(equipment["price"]?.text))}
                  </strong>
                </Td>
              </Tr>
            ))}
          </Tbody>
        </Table>
      </TableContainer>
    </>
  );
};

问题根源

  1. 排序逻辑互相抵消:handleHeaderClick中两个if语句会连续执行。当order为"ASC"时,第一个if执行后将order设为"DSC",紧接着第二个if检测到order变化,又把数据排序回原状态,相当于没有执行任何有效操作。
  2. 未跟踪当前排序列:没有记录当前是哪一列在排序,切换不同列时会沿用之前的排序状态,导致逻辑混乱。
  3. 特殊字段排序错误:regular-price和price是包含text属性的对象,直接调用toLowerCase()会报错(虽未触发,但属于潜在问题)。
  4. 初始状态未同步props更新:useState(rawData)仅在组件初始化时赋值,父组件data更新时tableData不会同步刷新。

修复方案

以下是修复后的完整代码:

export const MainTable = ({ data, isLoading, error }: MainTableProps) => {
  ...

  // Sorting logic
  const rawData = data.map((group) => group.equipments).flat();
  const [tableData, setTableData] = useState(rawData);
  const [order, setOrder] = useState("ASC");
  // 记录当前排序的列
  const [sortedCol, setSortedCol] = useState<string | null>(null);

  // 监听data变化,同步更新表格数据
  useEffect(() => {
    const newRawData = data.map((group) => group.equipments).flat();
    setTableData(newRawData);
  }, [data]);

  const handleHeaderClick = (col: string) => {
    // 点击同一列切换排序方向,点击新列默认升序
    const newOrder = sortedCol === col ? (order === "ASC" ? "DSC" : "ASC") : "ASC";
    setSortedCol(col);
    setOrder(newOrder);

    let sorted = [...tableData];
    // 区分文本列和价格列的排序逻辑
    if (col === "regular-price" || col === "price") {
      // 价格列:提取text转数字排序
      sorted.sort((a, b) => {
        const numA = Number(a[col]?.text || 0);
        const numB = Number(b[col]?.text || 0);
        return newOrder === "ASC" ? numA - numB : numB - numA;
      });
    } else {
      // 文本列:按字符串排序,处理空值
      sorted.sort((a, b) => {
        const valA = (a[col] || "").toLowerCase();
        const valB = (b[col] || "").toLowerCase();
        if (valA > valB) return newOrder === "ASC" ? 1 : -1;
        if (valA < valB) return newOrder === "ASC" ? -1 : 1;
        return 0;
      });
    }
    setTableData(sorted);
  };

  ...

  return (
    <>
      <TableContainer>
        <Table variant="simple">
          <Thead>
            <Tr>
              <Th onClick={() => handleHeaderClick("manufacturer")}>
                {t("manu")}
              </Th>
              <Th onClick={() => handleHeaderClick("model")}>{t("model")}</Th>
              <Th
                onClick={() => handleHeaderClick("product-family-display-name")}
              >
                {t("desc")}
              </Th>
              <Th onClick={() => handleHeaderClick("serial-number")}>
                {t("serial")}
              </Th>
              <Th onClick={() => handleHeaderClick("state")}>{t("prov")}</Th>
              <Th onClick={() => handleHeaderClick("city")}>{t("city")}</Th>
              <Th
                onClick={() => handleHeaderClick("regular-price")}
                className="align-right"
              >
                {t("regPrice")}
              </Th>
              <Th
                onClick={() => handleHeaderClick("price")}
                className="align-right sale-header"
              >
                {t("salePrice")}
              </Th>
            </Tr>
          </Thead>
          <Tbody>
            {currentPageData.map((equipment: Equipment) => (
              <Tr key={equipment.id}>
                <Td>
                  {equipment.manufacturer.length > 0
                    ? equipment.manufacturer
                    : "N/A"}
                </Td>
                <Td>{equipment.model.length > 1 ? equipment.model : "N/A"}</Td>
                <Td>{equipment["product-family-display-name"]}</Td>
                <Td>{equipment["serial-number"]}</Td>
                <Td>{equipment.state}</Td>
                <Td>
                  <a
                    target="_blank"
                    href={`https://website.com/locations/${
                      equipment.city.indexOf(" ")
                        ? equipment.city.replace(/\s/g, "-")
                        : equipment.city
                    }`}
                  >
                    {equipment.city}
                  </a>
                </Td>
                <Td className="align-right reg-price">
                  {formatPrice(Number(equipment["regular-price"]?.text))}
                </Td>
                <Td className="align-right sale-price">
                  <strong>
                    {formatPrice(Number(equipment["price"]?.text))}
                  </strong>
                </Td>
              </Tr>
            ))}
          </Tbody>
        </Table>
      </TableContainer>
    </>
  );
};

修复要点

  • 将原有的两个if改为基于新排序方向的逻辑,避免状态更新后立即触发反向排序。
  • 新增sortedCol状态跟踪当前排序列,切换列时默认升序,同一列点击切换方向。
  • 针对价格列的特殊结构,单独处理排序逻辑,提取text字段转数字后比较。
  • 添加useEffect监听data变化,确保父组件数据更新时表格数据同步刷新。
  • 处理空值情况,避免在undefined上调用toLowerCase()导致报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:03:12