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

如何使用useMemo Hook实现表格按姓名排序,解决无效Hook报错问题

错误原因
  • 违反了React Hook的调用规则:useMemo 只能在React组件的顶层作用域调用,不能在事件回调函数(比如你的sortByName点击处理函数)、条件判断、循环内部调用,这就是触发invalid hook error的直接原因。
  • 现有逻辑缺少排序状态记录:你需要单独的状态存储当前的排序规则(排序字段、升降序方向),不能在点击回调里临时生成排序后的列表,生成后也没有用到返回的排序结果,渲染时依然用的是原始users数组。
正确实现方案

你可以按以下逻辑调整代码:

  1. 新增排序状态,记录当前的排序字段和升降序方向
  2. 将useMemo移到组件顶层,依赖原始用户列表和排序状态,自动计算排序后的用户列表
  3. 点击表头的处理函数仅负责更新排序状态,不用手动执行排序逻辑
  4. 渲染表格时使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 23:06:03