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

Next.js 12转13代码适配问题:无法使用useState及请求异常

Next.js 12 升级到13后 useState 错误的解决方法

错误原因

Next.js 13 默认启用 App Router,其中组件默认是服务器组件,而 useState 这类 React 客户端 Hooks 只能在客户端组件中使用。如果你的代码是 Pages Router 结构(比如当前的 pages/index.js),原本应该兼容,但如果是迁移到 App Router 或者依赖版本不匹配,就会触发这个错误。


方案一:继续使用 Pages Router(最小改动)

Next.js 13 保留了 Pages Router,你的原有代码属于这个结构,只需做以下检查:

  • 确认页面文件放在项目根目录的 pages 文件夹下(比如当前的 pages/index.js)
  • 确保 react 和 react-dom 版本升级到 18+(Next.js 13 的强制要求)
  • 检查 next.config.js 中没有强制启用 App Router 的配置(默认无需额外配置)

这种情况下,你的原有代码几乎不需要修改就能正常运行,若仍报错,可尝试删除 node_modules 和 .next 文件夹后重新安装依赖。


方案二:迁移到 App Router(推荐)

如果要切换到 Next.js 13 的新路由体系,需要做以下改造:

1. 标记客户端组件

在使用 Hooks 的组件顶部添加 'use client' 指令,告诉 Next.js 这是客户端组件:

'use client'; // 必须放在代码最顶部
import { useState } from 'react';
// 其他导入语句...

2. 拆分异步数据获取与客户端状态

App Router 中,异步组件不能同时是客户端组件,所以需要把数据获取和状态逻辑拆分成两部分:服务器组件负责获取初始数据,客户端子组件处理状态和交互。

改造后的完整代码

// app/page.js(App Router 的首页文件)
import Head from 'next/head';
import { loadData } from './api/post';
import {
  Section,
  Cover,
  SocialNetworks,
  BuyMeCoffee
} from '../components';
import PostSection from './PostSection';

const LOAD_MORE_STEP = 4;

// 服务器组件:异步获取初始数据
export default async function Home() {
  const { posts, total } = await loadData(0, LOAD_MORE_STEP);

  return (
    <div style={{ marginBottom: '1rem' }}>
      <Head>
        <title>My blog</title>
      </Head>
      <Section>
        <Cover title="Elena<br />Litvinova" />
        <SocialNetworks />
        <BuyMeCoffee />
      </Section>
      <PostSection initialPosts={posts} total={total} />
    </div>
  )
}
// app/PostSection.js(客户端子组件)
'use client';
import { useState } from 'react';
import {
  Section,
  Title,
  PostGrid,
  Post,
  Button
} from '../components';

const LOAD_MORE_STEP = 4;

export default function PostSection({ initialPosts, total }) {
  const [posts, setPosts] = useState(initialPosts);
  const [loadedAmount, setLoadedAmount] = useState(LOAD_MORE_STEP);
  const [loading, setLoading] = useState(false);

  const showLoadButton = total > loadedAmount;

  const getMorePosts = async () => {
    setLoading(true);
    try {
      const res = await fetch(`/api/post?start=${loadedAmount}&end=${loadedAmount + LOAD_MORE_STEP}`);
      const data = await res.json();
      // 使用函数式更新避免闭包问题
      setLoadedAmount(prev => prev + LOAD_MORE_STEP);
      setPosts(prev => [...prev, ...data.posts]);
      setLoading(false);
    } catch (error) {
      console.error(error);
      setLoading(false);
    }
  };

  return (
    <Section>
      <Title>New Post</Title>
      <PostGrid>
        {posts.map((post) => (
          <Post key={post._id} {...post} />
        ))}
      </PostGrid>
      {showLoadButton && (
        <div style={{ display: 'flex', justifyContent: 'center' }}>
          <Button disabled={loading} onClick={getMorePosts}>
            {loading ? '加载中...' : '加载更多文章...'}
          </Button>
        </div>
      )}
    </Section>
  );
}

额外优化

  • 使用函数式更新状态(setLoadedAmount(prev => prev + LOAD_MORE_STEP)),避免闭包导致的状态更新错误
  • 优化按钮的加载状态文本,提升用户体验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 03:55:16