NextJS动态路由无法解析GraphQL异步请求排查求助
Next.js动态路由用户查询问题修复
问题场景
设置localhost:3000/[username]形式的动态路由,通过GraphQL查询MongoDB:存在的用户名正常显示,不存在则提示“User {slug} Not Found”。已验证GraphQL查询逻辑(调用getUser()时,有效用户返回对象,无效用户返回null),但发现useEffect中的fetchData()从未执行(console.log无输出),怀疑异步方法使用错误。
原始代码
页面组件代码
import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import { getUser } from '@/lib/users'; import {subscribe} from "graphql"; const UserPage = () => { const router = useRouter(); const { slug } = router.query; const [isValidUser, setIsValidUser] = useState(false); useEffect(() => { const fetchData = async () => { console.log(fetchData) let result; if (slug === undefined || slug == null) { result = null; } else { result = Array.isArray(slug) ? slug[0] : slug; const user = await getUser(result); if (user) { setIsValidUser(true); } } return result; } if (slug) { fetchData().catch(console.error); } }, [slug]); return ( <> {isValidUser ? ( <p>User {slug} found!</p> ) : ( <p>User {slug} not found.</p> )} </> ); }; export default UserPage;
getUser GraphQL查询代码
import { apolloClient } from "@/pages/_app"; import { GET_USER_BY_USERNAME_OR_ADDRESS } from "../db/queries/userQueries"; import { ADD_USER } from "../db/mutations/userMutations"; const GET_USER_BY_USERNAME_OR_ADDRESS = gql` query GetUserByUsernameOrAddress($usernameOrAddress: String) { getUserByUsernameOrAddress(usernameOrAddress: $usernameOrAddress) { address bio username } } `; const getUser = async (addressOrUsername: string) => { try { const { data } = await apolloClient.query({ query: GET_USER_BY_USERNAME_OR_ADDRESS, variables: { addressOrUsername }, }); if ( data ){ return data; } else { return null; } } catch (error) { console.error(error); } return false; };
问题排查与修复
1. fetchData未执行的核心原因
Next.js客户端路由初始化时,router.query.slug初始值为undefined,此时useEffect触发但if (slug)条件不成立,fetchData不会执行。同时原日志写法错误(console.log(fetchData)打印函数本身而非执行日志),会误导判断函数是否执行。
2. 分步修复
(1)修正路由就绪判断与日志
添加router.isReady判断确保路由参数完全加载,同时修正日志内容:
useEffect(() => { const fetchData = async () => { console.log('执行fetchData,当前slug:', slug); // 修正日志 let targetSlug = Array.isArray(slug) ? slug[0] : slug; const user = await getUser(targetSlug); setIsValidUser(!!user); // 直接转换为布尔值 }; // 等待路由就绪且slug存在时执行查询 if (router.isReady && slug) { fetchData().catch(console.error); } }, [router.isReady, slug]);
(2)修复getUser返回值逻辑
原代码返回整个data对象,即使用户不存在,data本身仍存在,导致if (user)判断始终为true。应返回具体查询字段:
const getUser = async (addressOrUsername: string) => { try { const { data } = await apolloClient.query({ query: GET_USER_BY_USERNAME_OR_ADDRESS, variables: { addressOrUsername }, }); // 返回具体用户数据,不存在则为null return data.getUserByUsernameOrAddress || null; } catch (error) { console.error('查询用户失败:', error); return null; // 错误时统一返回null } };
(3)添加加载状态优化体验
避免初始页面显示User undefined not found,新增加载状态:
const UserPage = () => { const router = useRouter(); const { slug } = router.query; const [isValidUser, setIsValidUser] = useState(false); const [isLoading, setIsLoading] = useState(true); // 新增加载状态 useEffect(() => { const fetchData = async () => { setIsLoading(true); let targetSlug = Array.isArray(slug) ? slug[0] : slug; const user = await getUser(targetSlug); setIsValidUser(!!user); setIsLoading(false); }; if (router.isReady && slug) { fetchData().catch(console.error); } else { setIsLoading(false); } }, [router.isReady, slug]); if (isLoading) { return <p>Loading...</p>; } return ( <> {isValidUser ? ( <p>User {slug} found!</p> ) : ( <p>User {slug} not found.</p> )} </> ); };
(4)推荐方案:用getServerSideProps预获取数据
Next.js动态路由推荐在服务端预获取数据,避免客户端异步请求延迟:
import { getUser } from '@/lib/users'; const UserPage = ({ user, slug }) => { if (!user) { return <p>User {slug} not found.</p>; } return <p>User {slug} found!</p>; }; export async function getServerSideProps(context) { const { slug } = context.params; const targetSlug = Array.isArray(slug) ? slug[0] : slug; const user = await getUser(targetSlug); return { props: { user, slug: targetSlug, }, }; } export default UserPage;
补充说明:技术栈为NextJS、MongoDB、NextAuth
内容的提问来源于stack exchange,提问作者Potion
相关产品推荐
相关产品推荐

