代码之家  ›  专栏  ›  技术社区  ›  Tibo

如何在Next JS中的_app文件中获取会话数据?

  •  0
  • Tibo  · 技术社区  · 1 年前

    我有一个多租户的Next应用程序,你需要登录才能访问每个页面(除了登录页面)。对于身份验证,我使用Next auth,并使用 getServerSideProps
    问题是它变得非常重复,因为我在更改页面时总是获取用户数据,而且我只想做一次,所以我考虑过使用Context API,但我必须将其放在 _app.tsx 无法进行SSR的文件。

    这是我的 _app.tsx 文件:

    import { TooltipProvider } from '@/components';
    import '@/styles/globals.css';
    import {
      Hydrate,
      QueryClient,
      QueryClientProvider,
    } from '@tanstack/react-query';
    import { SessionProvider } from 'next-auth/react';
    import { Crimson_Text } from 'next/font/google';
    import { useState } from 'react';
    import { Toaster } from 'react-hot-toast';
    
    const crimson = Crimson_Text({
      subsets: ['latin'],
      weight: '400',
      variable: '--font-crimson',
    });
    
    function App({ Component, pageProps: { session, ...pageProps } }) {
      const [queryClient] = useState(
        () =>
          new QueryClient({
            defaultOptions: {
              queries: {
                cacheTime: Infinity,
                staleTime: Infinity,
                retry: false,
              },
            },
          }),
      );
    
      return (
        <SessionProvider session={session}>
          <QueryClientProvider client={queryClient}>
            <Hydrate state={pageProps.dehydratedState}>
              <Toaster />
              <TooltipProvider>
                <div
                  className={`relative min-h-screen text-stone-900 ${crimson.variable}`}
                >
                  <Component {...pageProps} />
                </div>
              </TooltipProvider>
            </Hydrate>
          </QueryClientProvider>
        </SessionProvider>
      );
    }
    
    export default App;
    

    这是我的 index.tsx 文件:

    import { Layout, List, Search } from '@/modules';
    import { Tenant } from '@prisma/client';
    import { prisma } from 'lib/prisma';
    import { GetServerSidePropsContext } from 'next';
    import { getServerSession } from 'next-auth';
    import { authOptions } from './api/auth/[...nextauth]';
    
    type Props = {
      email: string;
      tenant: Tenant;
      tenantId: string;
    };
    
    function Home({ email, tenant, tenantId }: Props) {
      return (
        <main className="bg-stone-200 h-screen">
          <Layout email={email} company={tenant.company}>
            <div className="my-12">
              <Search />
              <List tenantId={tenantId} />
            </div>
          </Layout>
        </main>
      );
    }
    
    export default Home;
    
    export async function getServerSideProps(context: GetServerSidePropsContext) {
      const session = await getServerSession(context.req, context.res, authOptions);
      if (!session) {
        return {
          redirect: {
            destination: `/login`,
            permanent: false,
          },
        };
      }
    
      const email =
        typeof session.user?.email === `string` ? session.user?.email : undefined;
    
      const user = await prisma.user.findUnique({
        where: { email },
        include: { tenant: { select: { company: true } } },
      });
    
      if (!user) {
        return {
          redirect: {
            destination: `/login`,
            permanent: false,
          },
        };
      }
    
      return {
        props: JSON.parse(JSON.stringify(user)),
      };
    }
    

    这是 settings.tsx 文件:

    import { getNodes } from '@/data';
    import { useNodes } from '@/hooks';
    import { General, Layout, Tags, Users } from '@/modules';
    import { Tenant, User } from '@prisma/client';
    import { QueryClient, dehydrate } from '@tanstack/react-query';
    import {
      Tab,
      TabGroup,
      TabList,
      TabPanels
    } from '@tremor/react';
    import { prisma } from 'lib/prisma';
    import { GetServerSidePropsContext } from 'next';
    import { getServerSession } from 'next-auth';
    import { authOptions } from './api/auth/[...nextauth]';
    
    type Props = {
      user: User & { tenant: Tenant };
    };
    
    function Settings({ user }: Props) {
      const { data: nodes, isLoading, isError, error } = useNodes(user.tenantId);
    
      if (isLoading) {
        return <div>Loading...</div>;
      }
    
      if (isError && error instanceof Error) {
        return <div>Error: {error.message}</div>;
      }
    
      return (
        <Layout email={user.email} company={user.tenant.company}>
          <section className="flex flex-col items-center p-4 pb-12">
            <h2
              className="font-serif text-5xl lowercase"
              style={{ fontVariant: 'small-caps' }}
            >
              Settings
            </h2>
            <TabGroup className="mt-6">
              <TabList variant="solid" className="w-full">
                <Tab>General</Tab>
                <Tab>Tags</Tab>
                <Tab>Users</Tab>
              </TabList>
              <TabPanels>
                <General nodes={nodes} />
                <Tags tenantId={user.tenantId} />
                <Users tenantId={user.tenantId} />
              </TabPanels>
            </TabGroup>
          </section>
        </Layout>
      );
    }
    
    export default Settings;
    
    export async function getServerSideProps(context: GetServerSidePropsContext) {
      const session = await getServerSession(context.req, context.res, authOptions);
    
      if (!session) {
        return {
          redirect: {
            destination: `/login`,
            permanent: false,
          },
        };
      }
    
      const email =
        typeof session.user?.email === `string` ? session.user?.email : undefined;
    
      const user = await prisma.user.findUnique({
        where: { email },
        include: { tenant: { select: { company: true } } },
      });
    
      if (user.role !== 'admin') {
        return {
          redirect: {
            destination: `/`,
            permanent: false,
          },
        };
      }
    
      const queryClient = new QueryClient();
    
      await queryClient.prefetchQuery(['nodes', user.tenantId], () =>
        getNodes(user.tenantId),
      );
    
      return {
        props: {
          user: JSON.parse(JSON.stringify(user)),
          dehydratedState: dehydrate(queryClient),
        },
      };
    }
    

    正如你所看到的,我需要在两个文件中都有用户信息(因为我至少需要 tenantId 我也使用相同的布局(的问题是,我也在登录页面上使用它,但道具为空)。 我想知道如何改进应用程序的结构,以避免不必要的获取?

    0 回复  |  直到 1 年前