RemixJS 2 保有登入狀態
呈上篇 RemixJS 2 以 session 實作登入機制,session 的資料存放在 cookie 裡,那要如何在頁面內判斷及保有登入的狀態?
在前端可以用原本 react 的處理方式,以 context 來保有狀態。在後端必須每個 request 都要檢查 cookie。
get-auth.ts
是將解讀 cookie 的功能寫成模組,用來判斷是否為登入狀態。
// app/modules/get-auth.ts
import { getMySession } from "~/modules/sessions";
async function getAuth(request: Request) {
const session = await getMySession(request);
const userId = session.get("userId") || "";
const nickname = session.get("nickname") || "";
return { userId, nickname, auth: !!userId };
// auth 屬性用來判斷是否登入
}
export default getAuth;
AuthContextProvider
// app/contexts/AuthContext.tsx
import React, { createContext } from "react";
export type AuthDataType = {
userId: string | undefined;
nickname: string | undefined;
};
export const AuthContext = createContext<AuthDataType>({
userId: "",
nickname: "",
});
type PropsType = {
userId: string | undefined;
nickname: string | undefined;
children: React.ReactNode;
};
export function AuthContextProvider({ userId, nickname, children }: PropsType) {
return (
<AuthContext.Provider value={{ userId, nickname }}>
{children}
</AuthContext.Provider>
);
}
將 <AuthContextProvider>
包住 <Outlet />
。
// app/root.tsx 部份內容
export async function loader({ request, params }: LoaderFunctionArgs) {
console.log("App loader");
return await getAuth(request);
}
export default function App() {
const loaderData = useLoaderData<typeof loader>()
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<AuthContextProvider userId={loaderData.userId} nickname={loaderData.nickname}>
<Outlet />
</AuthContextProvider>
<ScrollRestoration />
<Scripts />
<LiveReload />
</body>
</html>
);
}
防止未授權而使用功能
這裡要分兩個部份 loader 和 action。loader 因為有階層關係,可以在 Layout 的 loader 做阻擋的動作。
// app/routes/address-book.tsx 的 loader 部份
export async function loader({ request, params }: LoaderFunctionArgs) {
console.log("address-book loader");
const myAuth = await getAuth(request);
if (!myAuth.auth) {
// 沒有登入,轉到登入頁面
return redirect(`/login?u=${request.url}`);
}
return null;
}
action 就必須在每個頁面處理。
export async function action({ request, params }: ActionFunctionArgs) {
const myAuth = await getAuth(request);
if (!myAuth.auth) {
// 沒有登入,轉到登入頁面
return redirect(`/login?u=${request.url}`);
}
// 略
}