Next.js集成Firestore获取详情报错:doc()路径为空
问题原因与解决方案
核心原因
- Next.js App Router路由参数获取方式错误:你使用的是App Router(
useRouter来自next/navigation),但仍沿用Pages Router的router.query来获取动态路由参数。在App Router中,动态路由(如/movie/[id])的参数需通过页面组件的props直接接收params,而非依赖router.query;且router.query在App Router初始加载时会是undefined,因为客户端组件需要等待路由状态稳定后才能拿到参数。 - Firebase初始化重复冗余:详情页和列表页都在
useEffect里重复初始化Firebase,应直接导入你在firebase.js中配置好的db实例,避免重复初始化的潜在问题。 - 缺少参数存在性校验:调用Firestore的
doc()前未检查id是否存在,空ID直接触发了Function doc() cannot be called with an empty path错误。
修复步骤
1. 修改详情页,通过props获取动态路由参数
确保详情页文件路径为app/movie/[id]/page.js,然后修改组件代码:
"use client"; import React, { useState, useEffect } from "react"; import { doc, getDoc } from "firebase/firestore"; import db from "../../firebase"; // 直接导入已初始化的db const MovieDetail = ({ params }) => { // 接收动态路由参数props const [movieDetails, setMovieDetails] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { const fetchMovieDetails = async () => { // 先校验ID是否有效 if (!params.id) { setError("无效的电影ID"); return; } setIsLoading(true); setError(null); try { const docRef = doc(db, "movies", params.id); const docSnap = await getDoc(docRef); if (docSnap.exists()) { setMovieDetails(docSnap.data()); } else { setError("未找到该电影"); } } catch (error) { console.error("获取电影详情失败:", error); setError("获取电影详情失败: " + error.message); } finally { setIsLoading(false); } }; fetchMovieDetails(); }, [params.id]); // 依赖params.id监听变化 return ( <div> {isLoading ? ( <p>加载中...</p> ) : error ? ( <p>{error}</p> ) : movieDetails ? ( <h1>{movieDetails.title}</h1> ) : ( <p>无电影信息</p> )} </div> ); }; export default MovieDetail;
2. 优化列表页的Firebase使用
移除列表页中重复的Firebase初始化代码,直接导入已配置的db:
"use client"; import React, { useState, useEffect } from "react"; import { collection, getDocs } from "firebase/firestore"; import Image from "next/image"; import Link from "next/link"; import db from "../firebase"; // 导入已初始化的db import Search from "@/components/Search/Search"; import "./s.css"; const Page = () => { const [movies, setMovies] = useState([]); useEffect(() => { const fetchData = async () => { try { console.log("开始获取数据..."); const moviesCollection = await getDocs(collection(db, "movies")); const moviesData = moviesCollection.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); setMovies(moviesData); console.log("数据获取成功:", moviesData); } catch (error) { console.error("获取数据失败:", error); } }; fetchData(); }, []); return ( <div> <Search /> <main> <div className="hero-container"> {movies.map((movie) => ( <div key={movie.id} className="main-container"> <div className="poster-container"> <Link href={`/movie/${movie.id}`}> <Image src={movie.posterUrl} className="poster" width={230} height={350} alt={movie.title} /> <p> {movie.title} ({movie.year}) </p> </Link> </div> </div> ))} </div> </main> </div> ); }; export default Page;
额外说明
- App Router的动态路由参数必须通过页面组件的
paramsprops获取,这是官方推荐的可靠方式,避免router.query的异步延迟问题。 - 确认你的动态路由文件结构符合App Router规范:
app/movie/[id]/page.js,[id]文件夹名对应路由参数名。
内容的提问来源于stack exchange,提问作者Yacine Ayechi
相关产品推荐
相关产品推荐

