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

Next.js SSR场景下如何避免属性透传(Prop Drilling)

在SSR场景中避免属性透传(Prop Drilling)的方案

针对你给出的Next.js App Router SSR场景(RootLayout为服务器组件,需给Navigation内部嵌套组件传递user数据),以下是几种实用方案:

1. React Context(适配混合服务器/客户端组件)

这是最直接的方案,既能覆盖客户端组件,也能配合服务器组件使用:

步骤1:创建User Context

新建app/contexts/UserContext.tsx:

'use client';

import { createContext, useContext, ReactNode } from 'react';

// 定义你的用户类型
type User = {
  id: string;
  name: string;
  email: string;
};

type UserContextType = {
  user: User | null;
};

const UserContext = createContext<UserContextType | undefined>(undefined);

export function UserProvider({ children, user }: { children: ReactNode; user: User | null }) {
  return (
    <UserContext.Provider value={{ user }}>
      {children}
    </UserContext.Provider>
  );
}

export function useUser() {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error('useUser必须在UserProvider内部使用');
  }
  return context;
}

步骤2:在RootLayout中提供Context

修改app/layout.tsx,把Navigation和子组件包裹在UserProvider中:

import { UserProvider } from './contexts/UserContext';

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const user = await getUser(cookies().get("accessToken")?.value);

  return (
    <html lang="en">
      <body>
        <UserProvider user={user}>
          <Navigation />
          {children}
        </UserProvider>
      </body>
    </html>
  );
}

步骤3:在嵌套组件中使用

比如Navigation内部的客户端组件UserProfile.tsx:

'use client';

import { useUser } from '../contexts/UserContext';

export default function UserProfile() {
  const { user } = useUser();
  
  if (!user) return <div>未登录</div>;
  
  return <div>欢迎,{user.name}</div>;
}

2. React Server Context(纯服务器组件场景)

如果Navigation及其所有嵌套组件都是服务器组件,用React 18+提供的createServerContext更高效,不需要客户端hydration:

步骤1:创建Server Context

新建app/contexts/UserServerContext.tsx:

import { createServerContext, ReactNode } from 'react';

type User = {
  id: string;
  name: string;
  email: string;
};

const UserServerContext = createServerContext<User | null>(null);

export function UserServerProvider({ children, user }: { children: ReactNode; user: User | null }) {
  return (
    <UserServerContext.Provider value={user}>
      {children}
    </UserServerContext.Provider>
  );
}

export function getUserFromServerContext() {
  return UserServerContext.use();
}

步骤2:在RootLayout中提供Context

修改app/layout.tsx:

import { UserServerProvider } from './contexts/UserServerContext';

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const user = await getUser(cookies().get("accessToken")?.value);

  return (
    <html lang="en">
      <body>
        <UserServerProvider user={user}>
          <Navigation />
          {children}
        </UserServerProvider>
      </body>
    </html>
  );
}

步骤3:在服务器组件中获取

比如嵌套的服务器组件NestedServerComponent.tsx:

import { getUserFromServerContext } from '../contexts/UserServerContext';

export default async function NestedServerComponent() {
  const user = getUserFromServerContext();
  
  return user ? <div>用户ID:{user.id}</div> : null;
}

3. 状态管理工具(备选,适合复杂状态场景)

如果你的应用有复杂状态管理需求,比如跨页面共享用户状态,可以用Zustand、Redux等工具,但需要处理SSR hydration:

以Zustand为例:

步骤1:创建SSR兼容的store

'use client';

import { create } from 'zustand';
import { persist } from 'zustand/middleware';

type User = {
  id: string;
  name: string;
  email: string;
};

type UserState = {
  user: User | null;
  setUser: (user: User | null) => void;
};

export const useUserStore = create<UserState>()(
  persist(
    (set) => ({
      user: null,
      setUser: (user) => set({ user }),
    }),
    {
      name: 'user-storage',
    }
  )
);

步骤2:在RootLayout中hydrate状态

新建客户端组件UserHydrator.tsx:

'use client';

import { useEffect } from 'react';
import { useUserStore } from '../stores/userStore';

export default function UserHydrator({ user }: { user: User | null }) {
  const setUser = useUserStore((state) => state.setUser);
  
  useEffect(() => {
    setUser(user);
  }, [user, setUser]);
  
  return null;
}

修改app/layout.tsx:

import UserHydrator from './UserHydrator';

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const user = await getUser(cookies().get("accessToken")?.value);

  return (
    <html lang="en">
      <body>
        <UserHydrator user={user} />
        <Navigation />
        {children}
      </body>
    </html>
  );
}

步骤3:在组件中使用store

'use client';

import { useUserStore } from '../stores/userStore';

export default function UserProfile() {
  const user = useUserStore((state) => state.user);
  
  if (!user) return <div>未登录</div>;
  
  return <div>欢迎,{user.name}</div>;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 19:17:46