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

Next.js最新App Router组件无限重渲染问题求助

问题:Next.js App Router 组件初始无限重渲染,不想使用 Suspense

使用最新版 Next.js App Router,组件加载数据时出现初始无限重渲染,数据本身是有效的。添加 React Suspense 可以解决该问题,但不想使用——因为重渲染时会显示 fallback 组件,希望实现正常重渲染而非无限循环。

相关代码

SidebarContent.tsx

"use client";
import { useEffect, useState } from "react";
import SlidebarItems from "./SlidebarItems";

export default function SlidebarContent() {
  const [content, setContent] = useState<any>([]);

  useEffect(() => {
    async function getData() {
      const res = await fetch("/api");
      const data = await res.json();
      setContent(data);
    }
    getData();
  }, []);

  const selectItem = (id: string) => {
    const newContent = content.map((item) => {
      if (item.id === id) {
        return { ...item, show: true };
      } else {
        return { ...item, show: false };
      }
    });
    setContent(newContent);
  };
  console.log("renders content");

  return (
    <div className="flex justify-between items-center h-full w-full">
      <div className="sliderRef flex gap-2 items-center h-full w-[85%] overflow-x-hidden">
        hi
        {content &&
          content.map((item) => (
            <SlidebarItems
              key={item.id}
              content={item}
              onClick={(id) => selectItem(id)}
            />
          ))}
      </div>
    </div>
  );
}

SidebarItems.js

"use client";
import Image from "next/image";
const SlidebarItems = async ({ content, onClick }) => {
  return (
    <div
      className={`slidebar-content ${content.show ? "selected" : ""}`}
      onClick={() => onClick(content.id)}
    >
      <div className="flex flex-col items-center w-[98px] ">
        <Image
          src={content.src}
          alt={content.title}
          width={24}
          height={24}
          priority
          className="h-7 w-7 mb-2 "
        />
        <h3 className="text-xs font-medium">{content.title}</h3>
      </div>
    </div>
  );
};

export default SlidebarItems;

解决方案

核心原因

SlidebarItems被错误声明为async组件,但内部没有任何异步逻辑(比如await操作)。在Next.js App Router的客户端组件中,async组件会强制触发React Suspense机制,即使未显式包裹Suspense,这会导致组件反复进入“加载-渲染”循环,引发无限重渲染。

修复步骤

  1. 移除SlidebarItems的async关键字,因为它不需要执行异步操作:
"use client";
import Image from "next/image";
const SlidebarItems = ({ content, onClick }) => {
  return (
    <div
      className={`slidebar-content ${content.show ? "selected" : ""}`}
      onClick={() => onClick(content.id)}
    >
      <div className="flex flex-col items-center w-[98px] ">
        <Image
          src={content.src}
          alt={content.title}
          width={24}
          height={24}
          priority
          className="h-7 w-7 mb-2 "
        />
        <h3 className="text-xs font-medium">{content.title}</h3>
      </div>
    </div>
  );
};

export default SlidebarItems;
  1. 确认状态逻辑无异常:当前SidebarContent中的useEffect依赖为空数组,只会执行一次数据获取;selectItem通过map生成新数组更新状态的逻辑也正确,不会触发意外的状态更新。

额外优化建议

  • 给content定义明确的TypeScript类型,替换any,提升代码可维护性:
interface SidebarItem {
  id: string;
  show: boolean;
  src: string;
  title: string;
}

// 初始化状态时指定类型
const [content, setContent] = useState<SidebarItem[]>([]);
  • 在数据请求中添加错误处理,避免请求失败导致的状态异常:
useEffect(() => {
  async function getData() {
    try {
      const res = await fetch("/api");
      if (!res.ok) throw new Error("数据请求失败");
      const data = await res.json();
      setContent(data);
    } catch (err) {
      console.error("获取侧边栏数据出错:", err);
    }
  }
  getData();
}, []);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 10:35:19