如何使用useMemo Hook实现表格按姓名排序,解决无效Hook报错问题
错误原因
- 违反了React Hook的调用规则:
useMemo只能在React组件的顶层作用域调用,不能在事件回调函数(比如你的sortByName点击处理函数)、条件判断、循环内部调用,这就是触发invalid hook error的直接原因。 - 现有逻辑缺少排序状态记录:你需要单独的状态存储当前的排序规则(排序字段、升降序方向),不能在点击回调里临时生成排序后的列表,生成后也没有用到返回的排序结果,渲染时依然用的是原始
users数组。
正确实现方案
你可以按以下逻辑调整代码:
- 新增排序状态,记录当前的排序字段和升降序方向
- 将
useMemo移到组件顶层,依赖原始用户列表和排序状态,自动计算排序后的用户列表 - 点击表头的处理函数仅负责更新排序状态,不用手动执行排序逻辑
- 渲染表格时使用
useMemo返回的排序后的数组,而非原始users
修正后的完整代码如下:
import "./styles.css"; import React, { useState, useEffect, useMemo } from "react"; import axios from "axios"; export default function App() { const [users, setUsers] = useState([]); // 新增排序状态:key是排序字段,direction是升降序asc/desc const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); const flattenObj = (obj) => { let result = {}; for (const i in obj) { if (typeof obj[i] === "object" && !Array.isArray(obj[i])) { const temp = flattenObj(obj[i]); for (const j in temp) { result[i + "_" + j] = temp[j]; } } else { result[i] = obj[i]; } } return result; }; useEffect(() => { getData(); }, []); async function getData() { try { let result = await axios.get("https://randomuser.me/api/?results=20"); let resultData = result.data.results.map((data) => flattenObj(data)); setUsers(resultData); } catch (err) { console.error(err); } } // 点击表头只更新排序状态 const sortByName = () => { setSortConfig(prev => { // 如果之前已经按姓名字段排序,就切换升降序,否则默认升序 if (prev.key === 'name_first') { return { key: 'name_first', direction: prev.direction === 'asc' ? 'desc' : 'asc' } } return { key: 'name_first', direction: 'asc' } }); }; // useMemo移到组件顶层,自动计算排序后的列表 const sortedUsers = useMemo(() => { // 没有排序规则时直接返回原列表 if (!sortConfig.key) return users; // 拷贝原数组避免修改原状态 const clonedUsers = [...users]; clonedUsers.sort((a, b) => { const compareResult = a[sortConfig.key].localeCompare(b[sortConfig.key]); // 降序的话把结果取反 return sortConfig.direction === 'asc' ? compareResult : -compareResult; }); return clonedUsers; }, [users, sortConfig]); return ( <div className="App"> <table> <thead> <tr> <th scope="column" onClick={sortByName}> 姓名 {sortConfig.key === 'name_first' && (sortConfig.direction === 'asc' ? '↑' : '↓')} </th> <th scope="column">Location Street Number</th> <th scope="column">Location City</th> </tr> </thead> <tbody> {/* 渲染排序后的列表 */} {sortedUsers.map((value, idx) => ( <tr key={idx}> <td>{value.name_first}</td> <td>{value.location_street_number}</td> <td>{value.location_city}</td> </tr> ))} </tbody> </table> </div> ); }
内容的提问来源于stack exchange,提问作者Judoboy Alex
相关产品推荐
相关产品推荐

