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

Next.js + TypeScript项目中API重复调用与分页异常问题求助

问题分析与解决方案

一、API被调用两次的原因

我看了你的代码,发现两个核心问题导致了API重复调用:

  1. useEffect依赖项触发重复执行
    你的useEffect依赖数组里包含了detail,而useEffect内部又调用了getDetailRole()——这个函数应该是用来更新detail状态的对吧?那流程就是:

    • 组件挂载时,useEffect因为id/form/detail初始值触发,调用getRolePermission(第一次API请求)
    • getDetailRole执行完成后更新detail状态,依赖项detail变化,导致useEffect再次执行,又调用了一次getRolePermission(第二次API请求)
  2. 重复声明router变量
    你写了两次const router = useRouter();,虽然这不会直接导致API调用两次,但属于冗余代码,建议删掉其中一个。

二、分页功能失效的原因

分页异常主要是因为状态更新的异步特性导致的状态不同步,还有逻辑上的小漏洞:

  1. 状态更新依赖旧值,未使用函数式更新
    你在更新rolePermission和pagination的时候,直接依赖了当前的状态变量:

    setRolePermission(rps.page === 1 ? roleP : [...rolePermission, ...roleP]);
    setPagination({ ...pagination, page: pagination.page + 1 });
    

    因为React的setState是异步的,当连续触发分页加载时,这里拿到的rolePermission和pagination可能还是旧值,导致数据拼接错误、页码计算混乱。

  2. useEffect重复触发打乱分页状态
    刚才说的useEffect因为detail变化重复执行,会重新调用getRolePermission,这会重置分页的状态(比如把页码又改回1),自然会导致分页逻辑混乱。

  3. 缺少对nextPage的判断逻辑
    当nextPage为0(没有更多数据)时,你的代码没有停止加载的判断,可能会继续发起无效请求,或者导致重复加载同一页数据。

三、修复后的代码示例

我把这些问题都修复了,你可以参考:

const EditRoles = () => { 
  const router = useRouter(); // 删掉重复的声明
  const { id } = router.query; 
  const [detail, setDetail] = useState<RoleDetail | undefined>(); // 增加类型定义
  const [rolePermission, setRolePermission] = useState<RolePermissionItem[]>([]); // 增加类型定义
  const [pagination, setPagination] = useState<Params>({ page: 1, row: 10, }); 
  const [loading, setLoading] = useState(false); 
  const [nextPage, setNextPage] = useState(1); // 初始化nextPage为1,确保首次加载触发

  const getDetailRole = async () => {
    if (!id) return;
    try {
      const res = await httpService.get(`${apiUrl.user}/v1/role/${id}`);
      setDetail(res.data);
      form.setFieldsValue({ name: res.data });
    } catch (err) {
      console.error('获取角色详情失败:', err);
    }
  };

  const getRolePermission = async (payload: { page?: number; row?: number; search?: string }) => { 
    if (!id || loading || nextPage === 0) return; // 增加拦截条件,避免无效请求
    setLoading(true); 
    try {
      const res = await httpService.get(`${apiUrl.user}/v1/role-permission/${id}`, { params: payload }); 
      const rps = res.data; 
      // 使用函数式更新,确保拿到最新的状态
      setRolePermission(prev => rps.page === 1 ? rps.data : [...prev, ...rps.data]); 
      setNextPage(rps.nextPage); 
      setPagination(prev => ({ ...prev, page: prev.page + 1 })); 
    } catch (err) {
      console.error('获取权限列表失败:', err);
    } finally {
      setLoading(false); 
    }
    console.log('this next page: ', rps.nextPage); 
  }; 

  useEffect(() => { 
    if (!id) return;
    getDetailRole(); 
    // 初始化分页状态,只在id变化时触发
    setRolePermission([]);
    setPagination({ page: 1, row: 10 });
    setNextPage(1);
    getRolePermission({ page: 1, row: 10 }); 
  }, [id]); // 移除detail和form依赖,避免重复触发

  // 单独处理表单赋值,避免依赖项混乱
  useEffect(() => {
    if (detail && rolePermission.length > 0) {
      form.setFieldsValue({ name: detail, permissions: rolePermission });
    }
  }, [detail, rolePermission, form]);

  return ( 
    // ... HTML GOES HERE ... 
  ); 
}; 
export default EditRoles;

额外建议

  • 给所有useState加上明确的类型定义,避免TypeScript类型报错
  • 分页加载更多时,一定要通过loading状态拦截重复请求,防止并发问题
  • 可以把分页逻辑封装成自定义Hook,让组件代码更简洁易维护

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 01:12:27