feat: 前端添加user状态管理
This commit is contained in:
25
apps/frontend/app/ClientProvider.tsx
Normal file
25
apps/frontend/app/ClientProvider.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useUserStore } from '@/store/useUserStore';
|
||||||
|
import { User } from '@/lib/types/user';
|
||||||
|
|
||||||
|
export function ClientProvider({
|
||||||
|
initialUser,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
initialUser: User | null;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
const setUser = useUserStore((state) => state.setUser);
|
||||||
|
const setInitialized = useUserStore((state) => state.setInitialized);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialUser) {
|
||||||
|
setUser(initialUser);
|
||||||
|
}
|
||||||
|
setInitialized();
|
||||||
|
}, [initialUser, setUser, setInitialized]);
|
||||||
|
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ import "./globals.css";
|
|||||||
import { ThemeProvider } from "../components/theme-provider";
|
import { ThemeProvider } from "../components/theme-provider";
|
||||||
import { metadata } from "./config/metadata";
|
import { metadata } from "./config/metadata";
|
||||||
import { Toaster } from "sonner";
|
import { Toaster } from "sonner";
|
||||||
|
import { UserAPI } from "@/lib/api/server";
|
||||||
|
import { ClientProvider } from "./ClientProvider";
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-geist-sans",
|
||||||
@@ -16,17 +18,20 @@ const geistMono = Geist_Mono({
|
|||||||
|
|
||||||
export { metadata };
|
export { metadata };
|
||||||
|
|
||||||
export default function RootLayout({
|
export default async function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
|
const user = await UserAPI.me().catch(() => null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
<body
|
<body
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased min-h-screen flex flex-col`}
|
className={`${geistSans.variable} ${geistMono.variable} antialiased min-h-screen flex flex-col`}
|
||||||
suppressHydrationWarning
|
suppressHydrationWarning
|
||||||
>
|
>
|
||||||
|
<ClientProvider initialUser={user}>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
defaultTheme="system"
|
defaultTheme="system"
|
||||||
@@ -38,6 +43,7 @@ export default function RootLayout({
|
|||||||
<Toaster />
|
<Toaster />
|
||||||
</main>
|
</main>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
</ClientProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
27
apps/frontend/store/useUserStore.ts
Normal file
27
apps/frontend/store/useUserStore.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// store/useUserStore.ts
|
||||||
|
import { User } from '@/lib/types/user';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { devtools } from 'zustand/middleware';
|
||||||
|
|
||||||
|
interface UserState {
|
||||||
|
user: User | null;
|
||||||
|
isLoading: boolean;
|
||||||
|
initialized: boolean;
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
setInitialized: () => void;
|
||||||
|
setUser: (user: User | null) => void;
|
||||||
|
clearUser: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUserStore = create<UserState>()(
|
||||||
|
devtools((set, get) => ({
|
||||||
|
user: null,
|
||||||
|
isLoading: false,
|
||||||
|
initialized: false,
|
||||||
|
|
||||||
|
setInitialized: () => set({ initialized: true }),
|
||||||
|
setUser: (user) => set({ user, isLoading: false }),
|
||||||
|
clearUser: () => set({ user: null }),
|
||||||
|
}))
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user