React实现点击表格表头(th)对tbody数据进行排序
实现点击表头排序的方案
我来给你一步步拆解怎么实现这个点击表头排序的功能,完全结合你现有的代码结构来改,非常直观:
核心思路
用React状态追踪当前的排序字段和排序方向(升序/降序),点击表头时更新这两个状态,之后根据状态对数据数组进行排序,最后把排序后的数组渲染到tbody里。
具体实现步骤
1. 初始化排序状态
在你的组件内部添加两个状态,用来记录当前的排序规则:
import { useState, useMemo } from 'react'; // 组件内部 const [sortKey, setSortKey] = useState('number'); // 默认按number字段排序 const [sortDirection, setSortDirection] = useState('asc'); // 默认升序
2. 给表头添加点击事件&排序标识
修改你现有的表头渲染逻辑,给每个th加上点击事件,同时可以添加一个小箭头来提示当前的排序方向:
<thead> <tr> {headTitles.map(title => ( <th key={title} onClick={() => handleSort(title)} style={{ cursor: 'pointer' }} // 鼠标变指针,提示可点击 > <Translate use={`connekt_champions_table_${title}_title`} /> {/* 可选:添加排序箭头,让用户直观看到当前排序状态 */} {sortKey === title && ( <span style={{ marginLeft: '4px' }}> {sortDirection === 'asc' ? '↑' : '↓'} </span> )} </th> ))} </tr> </thead>
然后实现handleSort函数,用来切换排序规则:
const handleSort = (title) => { // 如果点击的是当前正在排序的字段,就切换升/降序;否则默认设为升序 if (sortKey === title) { setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc'); } else { setSortKey(title); setSortDirection('asc'); } };
3. 对数据进行排序(用useMemo优化性能)
用useMemo缓存排序后的数组,避免每次组件渲染都重复排序,提升性能:
const sortedData = useMemo(() => { // 先复制原数据,避免直接修改原数组 const dataCopy = [...data]; dataCopy.sort((a, b) => { const valueA = a[sortKey]; const valueB = b[sortKey]; // 处理数字类型字段(比如number、mastery_level、points等) if (typeof valueA === 'number' && typeof valueB === 'number') { return sortDirection === 'asc' ? valueA - valueB : valueB - valueA; } // 处理字符串/日期类型字段(比如champion、last_played) if (typeof valueA === 'string' && typeof valueB === 'string') { // 先判断是不是日期格式,是的话转成Date对象排序 const dateA = new Date(valueA); const dateB = new Date(valueB); if (!isNaN(dateA.getTime()) && !isNaN(dateB.getTime())) { return sortDirection === 'asc' ? dateA - dateB : dateB - dateA; } // 普通字符串按字典序排序 return sortDirection === 'asc' ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA); } // 布尔类型字段(比如chest)可以单独处理,比如把true排前面 if (typeof valueA === 'boolean' && typeof valueB === 'boolean') { return sortDirection === 'asc' ? (valueA ? -1 : 1) : (valueA ? 1 : -1); } // 其他类型默认按升序 return 0; }); return dataCopy; }, [data, sortKey, sortDirection]);
4. 渲染排序后的数据
把原来tbody里的data.map替换成sortedData.map就行:
<tbody> {sortedData.map((item, i) => ( <tr key={item.number}> {/* 这里用item.number当唯一key,你可以换成实际的唯一标识字段 */} {/* 对应每个表头的单元格内容,比如: */} <td>{item.number}</td> <td>{item.champion}</td> <td>{item.mastery_level}</td> {/* 剩下的字段依次渲染... */} </tr> ))} </tbody>
额外优化小技巧
- 给表头加个hover样式,提升用户体验:
th:hover { background-color: #f0f0f0; transition: background-color 0.2s; }
- 如果有特殊格式的字段(比如progress的百分比),可以先提取数字再排序,比如
parseFloat(valueA)。
内容的提问来源于stack exchange,提问作者Krešimir Galić
相关产品推荐
相关产品推荐

