Next.js 13:如何正确重定向到动态路由避免显示过期数据?
问题
使用Next.js开发时,通过自定义Hook里的router.replace()/router.push()实现按钮点击重定向,目标页面显示过期数据,仅window.location.href能解决。已尝试设置dynamic: force-dynamic和fetch的cache: "no-store"但无效。
相关代码
自定义重定向Hook
import { useRouter } from "next/navigation"; export function useDynamicRedirect(statusId: number, itemId: number) { const router = useRouter(); const statusIdRedirectMap = { 1: `/pathA/${itemId}`, 2: `/pathB/${itemId}`, 10: `/pathA/${itemId}`, // Some special case }; const redirectPath = statusIdRedirectMap[statusId as keyof typeof statusIdRedirectMap]; return () => { if (redirectPath) { router.replace(redirectPath, { scroll: false, }); console.log(`Redirecting to ${redirectPath}`); // Temporary solution: // window.location.href = redirectPath; } else { console.warn(`No redirect path defined for statusId ${statusId}`); } }; }
表格组件中使用Hook
<Table.Body className="divide-y"> {data.map((item) => { const redirect = useDynamicRedirect(item.Status, item.Id); return ( <Table.Row key={item.Id} className="bg-white dark:border-gray-700 dark:bg-gray-800" > <Table.Cell></Table.Cell> {Object.entries(item).map(([key, value], index) => ( <Table.Cell className="w-full truncate p-3" key={index}> {key === "Status" ? getStatusDetails(value as number) : (value as string)} </Table.Cell> ))} <Table.Cell> <a className="cursor-pointer rounded-2xl bg-blue-500 p-2 font-medium text-white hover:bg-blue-600 dark:text-white" onClick={redirect} > View </a> </Table.Cell> </Table.Row> ); })} </Table.Body>
表格所在页面
import { DynamicTable } from "@/components/DynamicTable"; import { publicApiURL } from "@/constants/api"; export const dynamic = "force-dynamic"; export default async function DynamicPage() { const fetchData = async () => { const response = await fetch(`${publicApiURL}/Data/List`, { cache: "no-store", }); return response.json(); }; const items = await fetchData(); return <DynamicTable items={items} />; }
原因分析
- 违反React Hook规则:在
data.map循环内部调用useDynamicRedirect,Hook必须在组件顶层作用域调用,不能在循环、条件或嵌套函数中执行,这会导致Hook状态管理异常,引发路由行为不稳定。 - 目标页面缓存策略问题:
dynamic: force-dynamic和cache: "no-store"仅作用于当前表格页面,目标页面(/pathA/[itemId]、/pathB/[itemId])可能仍使用默认缓存策略(如force-cache)。Next.js客户端导航(router.push/replace)会复用已缓存的目标页面组件和数据,不会重新发起请求获取最新数据;而window.location.href会触发全页面刷新,绕过客户端缓存重新请求资源。
解决方案
1. 修复Hook调用位置
将useDynamicRedirect逻辑从循环中移出,改为在按钮点击时动态计算路径,或封装成非Hook工具函数:
// 替换原自定义Hook为带参数的回调函数 import { useRouter } from "next/navigation"; export function useDynamicRedirect() { const router = useRouter(); return (statusId: number, itemId: number) => { const statusIdRedirectMap = { 1: `/pathA/${itemId}`, 2: `/pathB/${itemId}`, 10: `/pathA/${itemId}`, }; const redirectPath = statusIdRedirectMap[statusId as keyof typeof statusIdRedirectMap]; if (redirectPath) { router.replace(redirectPath, { scroll: false }); console.log(`Redirecting to ${redirectPath}`); } else { console.warn(`No redirect path defined for statusId ${statusId}`); } }; }
在表格组件顶层调用Hook,循环中传入参数:
<Table.Body className="divide-y"> {/* 在组件顶层调用Hook */} const handleRedirect = useDynamicRedirect(); {data.map((item) => { return ( <Table.Row key={item.Id} className="bg-white dark:border-gray-700 dark:bg-gray-800"> {/* ... 其他单元格 ... */} <Table.Cell> <a className="cursor-pointer rounded-2xl bg-blue-500 p-2 font-medium text-white hover:bg-blue-600 dark:text-white" onClick={() => handleRedirect(item.Status, item.Id)} > View </a> </Table.Cell> </Table.Row> ); })} </Table.Body>
2. 配置目标页面的缓存策略
确保目标页面每次客户端导航都获取最新数据:
方式一:设置页面为动态渲染
在目标页面文件顶部添加:
export const dynamic = "force-dynamic";
方式二:在fetch请求中禁用缓存
目标页面的数据获取请求中设置cache: "no-store"或revalidate: 0:
export default async function PathAPage({ params }: { params: { itemId: string } }) { const response = await fetch(`${publicApiURL}/Data/Detail/${params.itemId}`, { cache: "no-store", // 禁用缓存,每次请求都获取最新数据 }); const data = await response.json(); return <div>{/* 渲染内容 */}</div>; }
方式三:客户端组件手动刷新数据(可选)
如果目标页面是客户端组件,可在页面加载时通过useEffect重新请求数据:
"use client"; import { useEffect, useState } from "react"; import { useParams } from "next/navigation"; export default function PathAPage() { const params = useParams(); const [data, setData] = useState(null); useEffect(() => { const fetchData = async () => { const response = await fetch(`${publicApiURL}/Data/Detail/${params.itemId}`, { cache: "no-store", }); const result = await response.json(); setData(result); }; fetchData(); }, [params.itemId]); if (!data) return <div>Loading...</div>; return <div>{/* 渲染内容 */}</div>; }
内容的提问来源于stack exchange,提问作者Ari
相关产品推荐
相关产品推荐

