Nextjs 动态路由问题 - 动态路由未显示正确的内容
Nextjs dynamic routes issue - dynamic routes not displaying proper content
我在 user
文件夹中有这个 [id].js
动态路由。我正在使用 getStaticPaths
来静态预呈现这些路径,然后使用 getStaticProps
来获取每个页面的数据。
但是,在生产环境中(因为这些功能 运行 在开发模式下的每个请求上,所以问题不存在),例如在 /user/a
到 /user/b
,它一直显示 /user/a
数据。
我也在使用 react-query
。我知道我可以通过在路由更改时刷新客户端的数据来解决这个问题,但现在我问:这不应该是使用 getStaticPaths
和创建动态路由时的 default/expected 行为吗? getStaticProps
?
所有动态路由都会发生这种情况,这里是 user
:
import React from "react";
import Image from "next/image";
import { dehydrate, QueryClient } from "react-query";
import { Layout, Posts } from "../../components";
import {
postsByUserQuery,
userQuery,
usersQuery,
postsSavedByUserQuery,
} from "../../utils/data";
import {
fetchPostsByUser,
fetchUser,
fetchPostsSavedByUser,
} from "../../utils/fetchers";
import { useData } from "../../hooks/useData";
import { client } from "../../client/client";
import { useRouter } from "next/router";
import { useSession, signOut } from "next-auth/react";
const UserProfile = () => {
const router = useRouter();
const { id } = router.query;
const { data: session } = useSession();
const { data: user } = useData("userInfo", fetchUser, id);
const {
data: postsByUser,
isFetching: isFetchingPostsByUser,
refetch: refetchPostsByUser,
} = useData("postsByUser", fetchPostsByUser, id);
const {
data: postsSavedByUser,
isFetching: isFetchingPostsSavedByUser,
refetch: refetchPostsSavedByUser,
} = useData("postsSavedByUser", fetchPostsSavedByUser, id);
return (
<Layout
title={`PostIt | ${user[0]?.userName} Profile`}
ogUrl={process.env.NEXT_PUBLIC_BASEURL + router.asPath}
ogType="article"
>
<div className="relative w-full h-[250px] 2xl:h-[350px]">
<Image
layout="fill"
placeholder="blur"
blurDataURL={`https://source.unsplash.com/1600x900/?${postsByUser[0]?.category}`}
src={`https://source.unsplash.com/1600x900/?${postsByUser[0]?.category}`}
alt="user-Cover-Pic"
objectFit="cover"
priority
/>
</div>
<section className="flex flex-col w-full gap-5 px-4 py-12 mx-auto max-w-7xl md:px-8 lg:px-10">
<div className="flex flex-col justify-center items-center gap-8">
<h1 className="text-center text-2xl 2xl:text-4xl text-white font-bold">
{user[0]?.userName}
</h1>
<div className="ring-2 ring-gray-100 p-1 flex items-center justify-center rounded-full w-60 h-60 2xl:w-80 2xl:h-80">
<div className="relative w-full h-full">
<Image
src={user[0]?.image}
layout="fill"
className="rounded-full"
alt="User Avatar"
objectFit="cover"
/>
</div>
</div>
{user[0]?._id === session?.user?.uid && (
<div className="w-full flex items-center justify-center">
<button
type="button"
className="mt-2 w-full max-w-[120px] rounded-lg text-base 2xl:text-lg text-white font-bold border-none outline-none bg-red-500 p-2 flex items-center justify-center transition duration-150 hover:bg-red-700"
onClick={() => signOut({ callbackUrl: "/login" })}
>
Logout
</button>
</div>
)}
<div className="flex flex-col gap-2 mt-5 w-full">
<span className="text-white text-base 2xl:text-xl font-bold">
Posts by {user[0]?.userName}{" "}
{postsByUser?.length > 0 ? `(${postsByUser.length})` : `(0)`}
</span>
{postsByUser?.length > 0 ? (
<Posts
posts={postsByUser}
refresh={refetchPostsByUser}
isFetching={isFetchingPostsByUser}
/>
) : (
<p className="text-sm 2xl:text-lg text-gray-400 w-full">
This user has not posted anything yet.
</p>
)}
</div>
<div className="flex flex-col gap-2 mt-5 w-full">
<span className="text-white text-base 2xl:text-xl font-bold">
Saved by {user[0]?.userName}{" "}
{postsSavedByUser?.length > 0
? `(${postsSavedByUser.length})`
: `(0)`}
</span>
{postsSavedByUser?.length > 0 ? (
<Posts
posts={postsSavedByUser}
refresh={refetchPostsSavedByUser}
isFetching={isFetchingPostsSavedByUser}
/>
) : (
<p className="text-sm 2xl:text-lg text-gray-400 w-full">
This user has not saved any post yet.
</p>
)}
</div>
</div>
</section>
</Layout>
);
};
export async function getStaticPaths() {
const query = usersQuery();
const users = await client.fetch(query);
const paths = users.map((user) => ({
params: { id: user._id },
}));
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const queryClient = new QueryClient();
const user = userQuery(params.id);
const postsByUser = postsByUserQuery(params.id);
const postsSavedByUser = postsSavedByUserQuery(params.id);
await queryClient.prefetchQuery("userInfo", () =>
client.fetch(user).then((data) => data)
);
await queryClient.prefetchQuery("postsByUser", () =>
client.fetch(postsByUser).then((data) => data)
);
await queryClient.prefetchQuery("postsSavedByUser", () =>
client.fetch(postsSavedByUser).then((data) => data)
);
return {
props: {
dehydratedState: dehydrate(queryClient),
},
revalidate: 30,
};
}
export default UserProfile;
编辑: useData
自定义挂钩:
import { useQuery } from "react-query";
import { toast } from "react-toastify";
export const useData = (queryKey, queryFn, queryFnParam1, queryFnParam2) => {
const { data, isFetching, refetch } = useQuery(
queryKey,
() => queryFn(queryFnParam1, queryFnParam2),
{
staleTime: 2.5 * 60 * 1000, //2.5 minutes
onError: (error) => {
toast.error(
`Couldn't establish connection due to error: ${error.message}`
);
},
}
);
return { data, isFetching, refetch };
};
您需要在 queryKey 中包含 userId。否则,所有请求都写入同一个缓存条目。 React Query Devtools 应该会告诉你这个。
我在 user
文件夹中有这个 [id].js
动态路由。我正在使用 getStaticPaths
来静态预呈现这些路径,然后使用 getStaticProps
来获取每个页面的数据。
但是,在生产环境中(因为这些功能 运行 在开发模式下的每个请求上,所以问题不存在),例如在 /user/a
到 /user/b
,它一直显示 /user/a
数据。
我也在使用 react-query
。我知道我可以通过在路由更改时刷新客户端的数据来解决这个问题,但现在我问:这不应该是使用 getStaticPaths
和创建动态路由时的 default/expected 行为吗? getStaticProps
?
所有动态路由都会发生这种情况,这里是 user
:
import React from "react";
import Image from "next/image";
import { dehydrate, QueryClient } from "react-query";
import { Layout, Posts } from "../../components";
import {
postsByUserQuery,
userQuery,
usersQuery,
postsSavedByUserQuery,
} from "../../utils/data";
import {
fetchPostsByUser,
fetchUser,
fetchPostsSavedByUser,
} from "../../utils/fetchers";
import { useData } from "../../hooks/useData";
import { client } from "../../client/client";
import { useRouter } from "next/router";
import { useSession, signOut } from "next-auth/react";
const UserProfile = () => {
const router = useRouter();
const { id } = router.query;
const { data: session } = useSession();
const { data: user } = useData("userInfo", fetchUser, id);
const {
data: postsByUser,
isFetching: isFetchingPostsByUser,
refetch: refetchPostsByUser,
} = useData("postsByUser", fetchPostsByUser, id);
const {
data: postsSavedByUser,
isFetching: isFetchingPostsSavedByUser,
refetch: refetchPostsSavedByUser,
} = useData("postsSavedByUser", fetchPostsSavedByUser, id);
return (
<Layout
title={`PostIt | ${user[0]?.userName} Profile`}
ogUrl={process.env.NEXT_PUBLIC_BASEURL + router.asPath}
ogType="article"
>
<div className="relative w-full h-[250px] 2xl:h-[350px]">
<Image
layout="fill"
placeholder="blur"
blurDataURL={`https://source.unsplash.com/1600x900/?${postsByUser[0]?.category}`}
src={`https://source.unsplash.com/1600x900/?${postsByUser[0]?.category}`}
alt="user-Cover-Pic"
objectFit="cover"
priority
/>
</div>
<section className="flex flex-col w-full gap-5 px-4 py-12 mx-auto max-w-7xl md:px-8 lg:px-10">
<div className="flex flex-col justify-center items-center gap-8">
<h1 className="text-center text-2xl 2xl:text-4xl text-white font-bold">
{user[0]?.userName}
</h1>
<div className="ring-2 ring-gray-100 p-1 flex items-center justify-center rounded-full w-60 h-60 2xl:w-80 2xl:h-80">
<div className="relative w-full h-full">
<Image
src={user[0]?.image}
layout="fill"
className="rounded-full"
alt="User Avatar"
objectFit="cover"
/>
</div>
</div>
{user[0]?._id === session?.user?.uid && (
<div className="w-full flex items-center justify-center">
<button
type="button"
className="mt-2 w-full max-w-[120px] rounded-lg text-base 2xl:text-lg text-white font-bold border-none outline-none bg-red-500 p-2 flex items-center justify-center transition duration-150 hover:bg-red-700"
onClick={() => signOut({ callbackUrl: "/login" })}
>
Logout
</button>
</div>
)}
<div className="flex flex-col gap-2 mt-5 w-full">
<span className="text-white text-base 2xl:text-xl font-bold">
Posts by {user[0]?.userName}{" "}
{postsByUser?.length > 0 ? `(${postsByUser.length})` : `(0)`}
</span>
{postsByUser?.length > 0 ? (
<Posts
posts={postsByUser}
refresh={refetchPostsByUser}
isFetching={isFetchingPostsByUser}
/>
) : (
<p className="text-sm 2xl:text-lg text-gray-400 w-full">
This user has not posted anything yet.
</p>
)}
</div>
<div className="flex flex-col gap-2 mt-5 w-full">
<span className="text-white text-base 2xl:text-xl font-bold">
Saved by {user[0]?.userName}{" "}
{postsSavedByUser?.length > 0
? `(${postsSavedByUser.length})`
: `(0)`}
</span>
{postsSavedByUser?.length > 0 ? (
<Posts
posts={postsSavedByUser}
refresh={refetchPostsSavedByUser}
isFetching={isFetchingPostsSavedByUser}
/>
) : (
<p className="text-sm 2xl:text-lg text-gray-400 w-full">
This user has not saved any post yet.
</p>
)}
</div>
</div>
</section>
</Layout>
);
};
export async function getStaticPaths() {
const query = usersQuery();
const users = await client.fetch(query);
const paths = users.map((user) => ({
params: { id: user._id },
}));
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const queryClient = new QueryClient();
const user = userQuery(params.id);
const postsByUser = postsByUserQuery(params.id);
const postsSavedByUser = postsSavedByUserQuery(params.id);
await queryClient.prefetchQuery("userInfo", () =>
client.fetch(user).then((data) => data)
);
await queryClient.prefetchQuery("postsByUser", () =>
client.fetch(postsByUser).then((data) => data)
);
await queryClient.prefetchQuery("postsSavedByUser", () =>
client.fetch(postsSavedByUser).then((data) => data)
);
return {
props: {
dehydratedState: dehydrate(queryClient),
},
revalidate: 30,
};
}
export default UserProfile;
编辑: useData
自定义挂钩:
import { useQuery } from "react-query";
import { toast } from "react-toastify";
export const useData = (queryKey, queryFn, queryFnParam1, queryFnParam2) => {
const { data, isFetching, refetch } = useQuery(
queryKey,
() => queryFn(queryFnParam1, queryFnParam2),
{
staleTime: 2.5 * 60 * 1000, //2.5 minutes
onError: (error) => {
toast.error(
`Couldn't establish connection due to error: ${error.message}`
);
},
}
);
return { data, isFetching, refetch };
};
您需要在 queryKey 中包含 userId。否则,所有请求都写入同一个缓存条目。 React Query Devtools 应该会告诉你这个。