我有一个多租户的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
我也使用相同的布局(的问题是,我也在登录页面上使用它,但道具为空)。
我想知道如何改进应用程序的结构,以避免不必要的获取?