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

Next.js中Firebase文档无法接收useRouter获取的动态ID路径问题

问题:Firebase无法通过useRouter动态获取的ID获取项目数据

我需要获取项目相关数据,但动态方式无法实现——Firebase不接受useRouter获取的ID。

动态获取ID的代码:

const router = useRouter();
const {projectID} = router.query;
const getProject = doc(firestore, `projects/${projectID}`);

静态指定ID时可以正常工作:

const getProject = doc(firestore, `projects/CEtVpthX9zMqUsyYSjEQ`);

我尝试过以下操作:

  • 对比路径字符串,控制台显示动态拼接的路径和静态路径完全相等,但动态方式依然无效
  • 试过多种变量拼接方式:
    const path = `projects/${projectID}`;
    const getProject = doc(firestore, path);
    
    const path = `${projectID}`;
    const getProject = doc(firestore, 'projects/' + path);
    
  • 使用react-router-dom的useParams获取ID,同样无法成功

测试代码如下:

const router = useRouter();
const {projectID} = router.query;
const path = `projects/${projectID}`;
const staticPath = `projects/CEtVpthX9zMqUsyYSjEQ`
console.log(path);
console.log(staticPath);
console.log(path===staticPath);
const getProject = doc(firestore, path);

动态路径输出结果与静态路径输出结果一致,但动态方式仍无法获取数据。

页面完整代码:

function Projects(): JSX.Element {
    const router = useRouter();
    const {projectID} = router.query;
    const path = `${projectID}`;
    const getProject = doc(firestore, `projects/${projectID}`);

    const [isLoading, setIsLoading] = useState(false);
    const [project, setProject] = useState({})

    useEffect(() => {
        const fetchProjectData = async () => {
            setIsLoading(true);
            const docSnap = await getDoc(getProject);
            if (docSnap.exists()){
                const newProjectObj = {
                    id: docSnap.id,
                    ...docSnap.data()
                };
                setProject(newProjectObj);
                setIsLoading(false);
                console.log(docSnap.data());
            } else{
                console.log("Нет данных")
            }
        }
        fetchProjectData();
        return 
    }, []);
    if(isLoading) return <div>Загрузка...</div>;
    return (
        <>
        <div className={cn(styles.heading, styles.headBlock)}>
            {Object.keys(project) && Object.keys(project).length ?
                <div>
                  <ProjectDetailed project={project}/>
                  <CallBackBlock></CallBackBlock>
                </div>
            :null}
            
        </div>
        </>
    );
}

export default withLayout(Projects);

问题原因分析

核心问题在于useEffect的依赖数组为空,且getProject的初始化时机不对:

  1. Next.js中router.query在页面初始加载时可能为undefined(服务端渲染/静态生成阶段没有路由参数),此时projectID是undefined,getProject会变成projects/undefined,但后续projectID更新时,由于useEffect没有监听依赖,不会重新执行数据请求。
  2. 即使控制台后来显示path和静态路径相等,那也是组件重新渲染后的结果,但此时useEffect已经执行过一次(用了错误的getProject),不会再触发。

解决方案

方案1:修复useEffect依赖与请求逻辑

function Projects(): JSX.Element {
    const router = useRouter();
    const {projectID} = router.query;
    const [isLoading, setIsLoading] = useState(false);
    const [project, setProject] = useState({})

    useEffect(() => {
        // 先判断projectID是否存在,避免无效请求
        if (!projectID) return;

        const fetchProjectData = async () => {
            setIsLoading(true);
            // 在useEffect内部创建getProject,确保使用最新的projectID
            const getProject = doc(firestore, `projects/${projectID}`);
            const docSnap = await getDoc(getProject);
            if (docSnap.exists()){
                const newProjectObj = {
                    id: docSnap.id,
                    ...docSnap.data()
                };
                setProject(newProjectObj);
                console.log(docSnap.data());
            } else{
                console.log("Нет данных")
            }
            setIsLoading(false);
        }
        fetchProjectData();
    }, [projectID]); // 监听projectID变化,参数更新时重新请求

    if(isLoading) return <div>Загрузка...</div>;
    // 处理project为空的情况
    if (!Object.keys(project).length) return <div>未找到项目数据</div>;
    
    return (
        <>
        <div className={cn(styles.heading, styles.headBlock)}>
            <div>
                <ProjectDetailed project={project}/>
                <CallBackBlock></CallBackBlock>
            </div>
        </div>
        </>
    );
}

export default withLayout(Projects);

方案2:使用useParams(如果是react-router-dom环境)

如果你的项目用的是react-router-dom而非Next.js的router,调整为:

import { useParams } from 'react-router-dom';

function Projects(): JSX.Element {
    const { projectID } = useParams<{ projectID: string }>();
    const [isLoading, setIsLoading] = useState(false);
    const [project, setProject] = useState({})

    useEffect(() => {
        if (!projectID) return;

        const fetchProjectData = async () => {
            setIsLoading(true);
            const getProject = doc(firestore, `projects/${projectID}`);
            const docSnap = await getDoc(getProject);
            if (docSnap.exists()){
                const newProjectObj = {
                    id: docSnap.id,
                    ...docSnap.data()
                };
                setProject(newProjectObj);
            } else{
                console.log("Нет данных")
            }
            setIsLoading(false);
        }
        fetchProjectData();
    }, [projectID]);

    // ... 其余渲染逻辑不变
}

关键注意点

  • 永远不要在组件顶层依赖可能变化的路由参数创建Firebase引用,因为组件初始化时参数可能未就绪
  • 必须将路由参数加入useEffect的依赖数组,确保参数更新时重新发起请求
  • 每次请求前先校验参数是否存在,避免发送无效请求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 23:15:41