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

Next.js中如何在客户端组件获取服务器组件的用户数据?

在Client Component中调用Server端validateRequest函数的正确方法

我有一个auth.ts文件里的validateRequest函数,用于获取用户登录后的详情,在Server Component中调用该函数获取数据的逻辑很顺畅,但在Client Component中直接套用相同逻辑却出现问题,希望了解正确的实现方法。

相关代码

auth.ts代码

export const validateRequest = cache(
  async (): Promise<
    { user: LuciaUser; session: Session } | { user: null; session: null } > => {
    await dbConnect();
    const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null;
    if (!sessionId) {
      return {
        user: null,
        session: null,
      };
    }

    const result = await lucia.validateSession(sessionId);

    if (!result) {
      return {
        user: null,
        session: null,
      };
    }

    const profileAttributes = await getProfileAttributes(result.user);

    if (!profileAttributes) {
      return {
        user: null,
        session: null,
      };
    }
    result.user.profile = {
      ...profileAttributes,
      //We return "id" instead of "_id" to be consistent with
      //the way lucia returns the user object.
      //aternatively, don't return profile id at all
      //depending on the requirements of the app
      _id: undefined,
      id: profileAttributes._id,
    };

    try {
      if (result.session && result.session.fresh) {
        const sessionCookie = lucia.createSessionCookie(result.session.id);
        cookies().set(
          sessionCookie.name,
          sessionCookie.value,
          sessionCookie.attributes
        );
      }
      if (!result.session) {
        const sessionCookie = lucia.createBlankSessionCookie();
        cookies().set(
          sessionCookie.name,
          sessionCookie.value,
          sessionCookie.attributes
        );
      }
    } catch (error) {
      console.log('error message', error);
    }
    return JSON.parse(JSON.stringify(result));
  }
);

Server Component中调用的代码

export default async function Home() {
  const { user } = await validateRequest();
return(

<>
{user? (
<h2 className="text-2xl font-semibold text-gray-900 dark:text-white">
   Welcome, {user?.profile?.firstName}!
</h2> ):(<>
<Link href="/auth/login">
 Go to Login
 <LogIn />
 </Link>
</>

)
}

</>

问题原因

直接在Client Component中调用validateRequest会失败,核心原因是:

  • 该函数使用了Server端专属API:cookies()、dbConnect(),这些API只能在Node.js环境运行,浏览器环境无法访问。
  • cache是Next.js的Server端缓存函数,Client端不支持该API。

正确实现方案

方案1:Server Component传参(推荐)

在父级Server Component中调用validateRequest获取用户数据,再将数据作为props传递给子级Client Component,无需额外网络请求,性能更优。

父级Server Component(示例:page.tsx)

import ClientComponent from './ClientComponent';
import { validateRequest } from './auth';
import Link from 'next/link';
import { LogIn } from '@/components/icons';

export default async function Home() {
  const { user } = await validateRequest();
  return (
    <div>
      <ClientComponent user={user} />
    </div>
  );
}

子级Client Component(示例:ClientComponent.tsx)

'use client';

import Link from 'next/link';
import { LogIn } from '@/components/icons';
import type { LuciaUser } from './auth'; // 导入你的类型定义

type Props = {
  user: LuciaUser | null;
};

export default function ClientComponent({ user }: Props) {
  return (
    <>
      {user ? (
        <h2 className="text-2xl font-semibold text-gray-900 dark:text-white">
          Welcome, {user.profile?.firstName}!
        </h2>
      ) : (
        <Link href="/auth/login">
          Go to Login
          <LogIn />
        </Link>
      )}
    </>
  );
}

方案2:API Route + Client端请求

创建一个Server端的API接口,在接口内部调用validateRequest,然后Client Component通过fetch或数据请求库(如SWR、React Query)获取数据,适合需要动态更新用户状态的场景(比如登录后无需刷新页面)。

步骤1:创建API Route(app/api/auth/validate/route.ts)

import { validateRequest } from '@/auth';
import { NextResponse } from 'next/server';

export async function GET() {
  const authResult = await validateRequest();
  return NextResponse.json(authResult);
}

步骤2:Client Component中调用(基础版)

'use client';

import { useState, useEffect } from 'react';
import Link from 'next/link';
import { LogIn } from '@/components/icons';
import type { LuciaUser, Session } from './auth';

type AuthResult = {
  user: LuciaUser | null;
  session: Session | null;
};

export default function ClientComponent() {
  const [auth, setAuth] = useState<AuthResult | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchAuthData = async () => {
      try {
        const res = await fetch('/api/auth/validate');
        const data = await res.json();
        setAuth(data);
      } catch (err) {
        console.error('获取用户数据失败:', err);
        setAuth({ user: null, session: null });
      } finally {
        setLoading(false);
      }
    };

    fetchAuthData();
  }, []);

  if (loading) return <div>加载中...</div>;

  return (
    <>
      {auth?.user ? (
        <h2 className="text-2xl font-semibold text-gray-900 dark:text-white">
          Welcome, {auth.user.profile?.firstName}!
        </h2>
      ) : (
        <Link href="/auth/login">
          Go to Login
          <LogIn />
        </Link>
      )}
    </>
  );
}

优化版:用SWR缓存请求

如果需要缓存请求结果、自动重新验证,可以使用SWR:

'use client';

import useSWR from 'swr';
import Link from 'next/link';
import { LogIn } from '@/components/icons';
import type { LuciaUser, Session } from './auth';

type AuthResult = {
  user: LuciaUser | null;
  session: Session | null;
};

const fetcher = async (url: string) => {
  const res = await fetch(url);
  return res.json() as Promise<AuthResult>;
};

export default function ClientComponent() {
  const { data: auth, error, isLoading } = useSWR<AuthResult>('/api/auth/validate', fetcher);

  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>获取用户数据失败</div>;

  return (
    <>
      {auth?.user ? (
        <h2 className="text-2xl font-semibold text-gray-900 dark:text-white">
          Welcome, {auth.user.profile?.firstName}!
        </h2>
      ) : (
        <Link href="/auth/login">
          Go to Login
          <LogIn />
        </Link>
      )}
    </>
  );
}

注意事项

  • 优先使用方案1,减少网络请求开销,适合大多数静态/半静态页面场景。
  • 方案2适合需要在Client端动态更新用户状态的场景,比如登录后即时刷新用户信息。
  • 永远不要在Client Component中直接调用Server端专属API(如cookies()、数据库操作),必须通过Server层中转。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 02:19:57