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

Next.js 13中服务端组件与子组件共享Supabase数据的最优方案

Next.js 13/React 服务端组件递归结构的数据共享方案

1. 用 React 的 createServerContext(首推)

React 18.2 之后新增了专门给服务端组件设计的上下文API,完全不需要依赖客户端逻辑,完美匹配你这种递归组件的场景。

具体操作:

  • 先创建服务端上下文文件:
// app/contexts/ServerDataContext.tsx
'use server';

import { createServerContext } from 'react';

// 定义数据类型,避免类型报错
type ServerDataContextType = {
  folderData: Array<{ id: string; name: string; children?: Array<any> }>;
};

export const ServerDataContext = createServerContext<ServerDataContextType>('server-data', {
  folderData: [],
});
  • 在父服务端组件中拉取Supabase数据并提供上下文:
// app/FolderTree.tsx
import { ServerDataContext } from './contexts/ServerDataContext';
import { supabase } from './supabase/client';
import FolderItem from './FolderItem';

export default async function FolderTree() {
  // 服务端拉取数据,全程在服务器执行,安全且高效
  const { data: folderData } = await supabase.from('folders').select('*');

  return (
    <ServerDataContext.Provider value={{ folderData }}>
      {/* 启动递归渲染 */}
      <FolderItem folder={folderData[0]} />
    </ServerDataContext.Provider>
  );
}
  • 递归子组件(服务端/客户端均可)直接消费上下文:
// app/FolderItem.tsx
// 若组件需要客户端交互则加'use client',纯展示的服务端组件可省略
'use client';

import { useContext } from 'react';
import { ServerDataContext } from './contexts/ServerDataContext';

export default function FolderItem({ folder }) {
  const { folderData } = useContext(ServerDataContext);

  return (
    <div>
      <span>{folder.name}</span>
      {/* 递归渲染子文件夹 */}
      {folder.children?.map(child => (
        <FolderItem key={child.id} folder={child} />
      ))}
    </div>
  );
}

划重点:如果子组件是纯服务端组件,无需加'use client',直接调用useContext即可。

2. 借助布局组件共享路由级数据

如果你的递归结构属于某个路由下的组件,可以利用Next.js App Router的布局组件特性——布局是服务端组件,可在其中获取数据,再传递给页面及所有子组件,递归组件也能轻松拿到数据。

// app/(dashboard)/layout.tsx
import { supabase } from '../supabase/client';
import type { ReactNode } from 'react';

export default async function DashboardLayout({ children }: { children: ReactNode }) {
  const { data: folderData } = await supabase.from('folders').select('*');

  // 将数据注入到子组件中
  return <div>{React.cloneElement(children as React.ReactElement, { folderData })}</div>;
}

这种方式适合路由级的全局数据共享,即便递归层级较深,也比手动逐层props drilling清爽很多。

3. 用Children.map批量给递归组件注入props

如果递归组件都是纯服务端组件,可以写个工具函数,遍历所有子节点批量注入数据,省去手动逐层传递的麻烦:

// app/FolderTree.tsx
import { supabase } from './supabase/client';
import FolderItem from './FolderItem';

export default async function FolderTree() {
  const { data: folderData } = await supabase.from('folders').select('*');

  // 递归注入数据的工具函数
  const injectData = (items) => {
    return items.map(item => {
      const newChildren = item.children ? injectData(item.children) : [];
      return <FolderItem key={item.id} folder={item} folderData={folderData} children={newChildren} />;
    });
  };

  return <div>{injectData(folderData)}</div>;
}

这种方式适合无客户端交互的纯服务端渲染递归结构。

关键注意事项

  • 服务端组件无法使用Zustand、Redux这类客户端状态管理工具,createServerContext是官方专为服务端组件打造的方案,完全适配。
  • 若递归组件需要客户端交互(比如点击展开/折叠),可将交互逻辑抽离为单独的'use client'组件,服务端数据通过上下文或props传入即可。
  • 所有Supabase数据请求务必在服务端组件中完成,不要放在客户端,既不安全又会影响性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:05:10