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

Next.js14 React应用路由跳转带状态及404问题排查

解决Next.js 14路由跳转404及页面状态传递问题

问题根源

你用npx create-next-app@latest创建的Next.js 14项目默认使用App Router(src/app目录),而新增的pages文件夹属于旧版的Pages Router。当项目同时存在src/app和pages时,App Router优先级更高,pages/dashboard.tsx不会被自动映射为/dashboard路由,导致跳转后出现404。

推荐方案:统一使用App Router(Next.js 14官方推荐)

1. 调整目录结构

将login、dashboard等页面迁移到App Router的规范结构中,每个页面对应一个文件夹+page.tsx:

src/
└── app/
    ├── page.tsx       # 原首页
    ├── login/
    │   └── page.tsx   # 登录页面
    └── dashboard/
        └── page.tsx   # 仪表盘页面

2. 实现页面跳转(替代react-router的navigate)

在App Router中,使用next/navigation提供的useRouter钩子完成跳转,注意需要在客户端组件中使用(添加'use client'指令):

// src/app/login/page.tsx
'use client';

import { useRouter } from 'next/navigation';

export default function LoginPage() {
  const router = useRouter();

  const handleLogin = () => {
    // 模拟登录逻辑,获取用户状态
    const userInfo = { username: 'testUser', token: 'xxx' };
    
    // 跳转至dashboard页面
    router.push('/dashboard');
  };

  return (
    <div>
      <h1>登录页</h1>
      <button onClick={handleLogin}>登录</button>
    </div>
  );
}

3. 页面间传递状态的三种常用方式

方式1:URL查询参数(适合非敏感的简单数据)

跳转时将状态拼接在URL中,dashboard页面直接读取:

// 登录页跳转逻辑
router.push(`/dashboard?username=${userInfo.username}`);

// src/app/dashboard/page.tsx
export default function DashboardPage({ searchParams }: { searchParams: { username?: string } }) {
  return (
    <div>
      <h1>仪表盘</h1>
      {searchParams.username && <p>欢迎,{searchParams.username}!</p>}
    </div>
  );
}

方式2:全局状态管理(适合敏感/复杂的用户状态)

用React Context或 Zustand、Redux等状态库存储全局状态,登录后更新状态,dashboard页面直接读取:

// src/app/context/UserContext.tsx
'use client';

import { createContext, useContext, useState, ReactNode } from 'react';

type UserContextType = {
  user: { username: string; token: string } | null;
  setUser: (user: { username: string; token: string } | null) => void;
};

const UserContext = createContext<UserContextType | undefined>(undefined);

export function UserProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<{ username: string; token: string } | null>(null);
  return <UserContext.Provider value={{ user, setUser }}>{children}</UserContext.Provider>;
}

export function useUser() {
  const context = useContext(UserContext);
  if (!context) throw new Error('useUser必须在UserProvider内部使用');
  return context;
}

在根布局中注入Provider:

// src/app/layout.tsx
import { UserProvider } from './context/UserContext';
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Next.js App',
  description: 'Generated by create next app',
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <UserProvider>{children}</UserProvider>
      </body>
    </html>
  );
}

登录页更新状态并跳转:

// src/app/login/page.tsx
'use client';

import { useRouter } from 'next/navigation';
import { useUser } from '../context/UserContext';

export default function LoginPage() {
  const router = useRouter();
  const { setUser } = useUser();

  const handleLogin = () => {
    const userInfo = { username: 'testUser', token: 'xxx' };
    setUser(userInfo);
    router.push('/dashboard');
  };

  return (
    <div>
      <h1>登录页</h1>
      <button onClick={handleLogin}>登录</button>
    </div>
  );
}

dashboard页面读取状态:

// src/app/dashboard/page.tsx
'use client';

import { useUser } from '../context/UserContext';

export default function DashboardPage() {
  const { user } = useUser();

  if (!user) {
    window.location.href = '/login';
    return null;
  }

  return (
    <div>
      <h1>仪表盘</h1>
      <p>欢迎,{user.username}!</p>
      <p>你的Token:{user.token}</p>
    </div>
  );
}

方式3:Cookie存储(适合需要持久化的敏感信息)

用Next.js的cookies API存储token这类敏感数据,支持服务器端读取:

// src/app/login/page.tsx
'use client';

import { useRouter } from 'next/navigation';
import { cookies } from 'next/headers';

export default function LoginPage() {
  const router = useRouter();

  const handleLogin = async () => {
    const userInfo = { username: 'testUser', token: 'xxx' };
    // 写入Cookie,设置安全属性
    const cookieStore = cookies();
    await cookieStore.set('userToken', userInfo.token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      maxAge: 60 * 60 * 24, // 有效期1天
    });
    router.push('/dashboard');
  };

  return (
    <div>
      <h1>登录页</h1>
      <button onClick={handleLogin}>登录</button>
    </div>
  );
}

dashboard页面读取Cookie(支持服务器端组件):

// src/app/dashboard/page.tsx
import { cookies } from 'next/headers';

export default function DashboardPage() {
  const cookieStore = cookies();
  const userToken = cookieStore.get('userToken')?.value;

  if (!userToken) {
    // 服务器端重定向到登录页
    return Response.redirect(new URL('/login', process.env.NEXT_PUBLIC_APP_URL));
  }

  return (
    <div>
      <h1>仪表盘</h1>
      <p>已登录,Token:{userToken}</p>
    </div>
  );
}

备选方案:切换为Pages Router(不推荐)

如果坚持使用pages文件夹,需要删除src/app目录(或修改next.config.js配置),然后使用Pages Router的路由方式:

// pages/login.tsx
import { useRouter } from 'next/router';

export default function LoginPage() {
  const router = useRouter();

  const handleLogin = () => {
    router.push({
      pathname: '/dashboard',
      query: { username: 'testUser' },
    });
  };

  return <button onClick={handleLogin}>登录</button>;
}
// pages/dashboard.tsx
import { useRouter } from 'next/router';

export default function DashboardPage() {
  const router = useRouter();
  const username = router.query.username;

  return <div>欢迎{username}</div>;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 12:42:44