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

Next.js中如何更新layout.tsx中非页面组件的数据?

问题:Next.js中更新Layout组件内的侧边栏项目列表

我正在学习Next.js,用Firebase开发一个简单的项目管理应用。我知道向数据库添加数据后可以用revalidatePath获取更新后的数据集,但不清楚怎么处理数据展示在layout.tsx组件(而非页面)里的场景。

具体场景:

  • 侧边栏展示当前登录用户的所有项目,该侧边栏属于layout.tsx,不属于单个页面
  • 有一个创建项目的表单页面,创建项目后需要更新侧边栏的项目列表

现有服务端代码

创建项目的服务端操作

"use server";

import { Project } from "@/app/models/Project";
import { db } from "@/config/firebase";
import { addDoc, collection } from "firebase/firestore";

export const CreateProject = async (formData: { name: string; color: string }, user: string) => {
  try {
    const collectionRef = collection(db, "projects");

    const projectRef = await addDoc(collectionRef, {
      name: formData.name,
      color: formData.color,
      user,
    });

    const project = {
      id: projectRef.id,
      name: formData.name,
      color: formData.color,
      user,
    };

    return project;
  } catch (error) {
    console.log("Error adding document: ", error);
  }
};

获取项目的服务端操作

"use server";

import { Project } from "@/app/models/Project";
import { db } from "@/config/firebase";
import { collection, getDocs, query, where } from "firebase/firestore";

export const GetProjects = async (user: string) => {
  try {
    const collectionRef = collection(db, "projects");

    const q = query(collectionRef, where("user", "==", user));

    const querySnapshot = await getDocs(q);

    const projects: Project[] = querySnapshot.docs.map((doc) => ({
      id: doc.id,
      name: doc.data().name,
      color: doc.data().color,
      user: doc.data().user,
    }));

    return projects;
  } catch (error) {
    console.log("Error getting documents: ", error);
  }
};

解决方案

方法1:使用revalidateTag触发布局数据刷新

因为layout.tsx是全局布局,不属于单个页面路径,用缓存标签替代路径验证更合适。

  1. 修改创建项目函数,添加标签验证
    在CreateProject中导入revalidateTag,创建项目后验证对应用户的项目标签:

    "use server";
    
    import { Project } from "@/app/models/Project";
    import { db } from "@/config/firebase";
    import { addDoc, collection } from "firebase/firestore";
    import { revalidateTag } from "next/cache"; // 导入revalidateTag
    
    export const CreateProject = async (formData: { name: string; color: string }, user: string) => {
      try {
        const collectionRef = collection(db, "projects");
        const projectRef = await addDoc(collectionRef, {
          name: formData.name,
          color: formData.color,
          user,
        });
        const project = {
          id: projectRef.id,
          name: formData.name,
          color: formData.color,
          user,
        };
        // 验证用户专属的项目标签,触发布局刷新
        revalidateTag(`projects-${user}`);
        return project;
      } catch (error) {
        console.log("Error adding document: ", error);
      }
    };
    
  2. 在Layout中给数据请求绑定缓存标签
    使用unstable_cache包裹GetProjects调用,关联对应的标签:

    // layout.tsx(Server Component)
    import { GetProjects } from "@/app/actions/projects";
    import { getCurrentUser } from "@/app/actions/auth";
    import { unstable_cache } from "next/cache";
    
    export default async function RootLayout({ children }: { children: React.ReactNode }) {
      const user = await getCurrentUser();
      // 绑定缓存标签,与创建时的标签对应
      const getCachedProjects = unstable_cache(
        async (userId) => GetProjects(userId),
        [`projects-${user.id}`],
        { tags: [`projects-${user.id}`] }
      );
      const projects = await getCachedProjects(user.id);
    
      return (
        <html lang="zh-CN">
          <body>
            <Sidebar projects={projects} />
            {children}
          </body>
        </html>
      );
    }
    

方法2:客户端组件+Firestore实时监听

如果需要即时更新,把侧边栏改成客户端组件,用Firestore的实时快照监听数据变化:

  1. 将侧边栏改为客户端组件

    // components/Sidebar.tsx
    'use client';
    
    import { useEffect, useState } from 'react';
    import { db } from '@/config/firebase';
    import { collection, query, where, onSnapshot } from 'firebase/firestore';
    import { Project } from '@/app/models/Project';
    
    export default function Sidebar({ userId }: { userId: string }) {
      const [projects, setProjects] = useState<Project[]>([]);
    
      useEffect(() => {
        // 创建查询并监听数据变化
        const q = query(collection(db, 'projects'), where('user', '==', userId));
        const unsubscribe = onSnapshot(q, (querySnapshot) => {
          const updatedProjects = querySnapshot.docs.map(doc => ({
            id: doc.id,
            name: doc.data().name,
            color: doc.data().color,
            user: doc.data().user,
          }));
          setProjects(updatedProjects);
        });
    
        // 组件卸载时取消监听
        return () => unsubscribe();
      }, [userId]);
    
      return (
        <div className="sidebar">
          <h3>我的项目</h3>
          {projects.map(project => (
            <div key={project.id} style={{ color: project.color }}>
              {project.name}
            </div>
          ))}
        </div>
      );
    }
    
  2. 在Layout中传递用户ID

    // layout.tsx(Server Component)
    import Sidebar from '@/components/Sidebar';
    import { getCurrentUser } from '@/app/actions/auth';
    
    export default async function RootLayout({ children }: { children: React.ReactNode }) {
      const user = await getCurrentUser();
      return (
        <html lang="zh-CN">
          <body>
            <Sidebar userId={user.id} />
            {children}
          </body>
        </html>
      );
    }
    

    这种方式无需手动触发刷新,Firestore会自动推送数据更新到客户端。

方法3:重定向触发页面刷新

如果不需要实时更新,创建项目后重定向到任意页面,会触发Layout重新渲染并获取最新数据:

"use server";

import { Project } from "@/app/models/Project";
import { db } from "@/config/firebase";
import { addDoc, collection } from "firebase/firestore";
import { redirect } from "next/navigation";

export const CreateProject = async (formData: { name: string; color: string }, user: string) => {
  try {
    const collectionRef = collection(db, "projects");
    await addDoc(collectionRef, {
      name: formData.name,
      color: formData.color,
      user,
    });
    // 重定向到项目列表页或表单页,触发Layout刷新
    redirect('/projects');
  } catch (error) {
    console.log("Error adding document: ", error);
  }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 08:41:00