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> </> ); };
问题根源
- 排序逻辑互相抵消:
handleHeaderClick中两个if语句会连续执行。当order为"ASC"时,第一个if执行后将order设为"DSC",紧接着第二个if检测到order变化,又把数据排序回原状态,相当于没有执行任何有效操作。 - 未跟踪当前排序列:没有记录当前是哪一列在排序,切换不同列时会沿用之前的排序状态,导致逻辑混乱。
- 特殊字段排序错误:
regular-price和price是包含text属性的对象,直接调用toLowerCase()会报错(虽未触发,但属于潜在问题)。 - 初始状态未同步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
相关产品推荐
相关产品推荐

