🐷
Remix / Firebase / Tailwind 環境構築する
環境変数を読むとかTailwind入れるとかあたり若干ややこしかったので、備忘録がてらRemixの環境構築について書きます。Firebaseのプロジェクトはあらかじめ作っておいてください。
Firebase導入まで
プロジェクト準備
このあたりを見ながらプロジェクトを立ち上げます。
npx create-remix@latest
cd 作ったプロジェクト名
npm run dev
必要なパッケージも導入しておきましょう。
npm i dotenv firebase zustand
touch .env
デフォルトの npm run dev
だと環境変数読めないので、devコマンドをちょっといじります。
package.json
"scripts": {
"build": "remix build",
- "dev": "remix dev",
+ "dev": "node -r dotenv/config node_modules/.bin/remix dev",
"postinstall": "remix setup node",
環境変数はこんな感じで埋めておきます。
FIREBASE_CLIENT_EMAIL=value
FIREBASE_PUBLIC_API_KEY=value
FIREBASE_AUTH_DOMAIN=value
FIREBASE_DATABASE_URL=value
FIREBASE_PROJECT_ID=value
Authentication
ログイン部分のUIはこちらにお任せするので入れておきましょう。
npm i --save react-firebaseui
コードをいじっていきます。
app/root.tsx
import {
json,
Links,
LiveReload,
LoaderFunction,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useLoaderData,
} from "remix";
import { FirebaseOptions } from "firebase/app";
import initFirebase from "../utils/auth/initFirebase";
import { useAuth } from "../utils/auth/useAuth";
export const loader: LoaderFunction = () => {
const config = {
apiKey: process.env.FIREBASE_PUBLIC_API_KEY,
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.FIREBASE_DATABASE_URL,
projectId: process.env.FIREBASE_PROJECT_ID,
};
return json(config);
};
export default function App() {
const config = useLoaderData<FirebaseOptions>();
initFirebase(config);
useAuth();
return (
<Document>
<Layout>
<Outlet />
</Layout>
</Document>
);
}
function Document({
children,
title,
}: {
children: React.ReactNode;
title?: string;
}) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
{title ? <title>{title}</title> : null}
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
{process.env.NODE_ENV === "development" && <LiveReload />}
</body>
</html>
);
}
function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<div>
<div className="container m-8">{children}</div>
</div>
</div>
);
}
まだ作ってないコードのimportがあるあたりご留意ください。すぐ作ります。
ポイントはloaderで環境変数を読んでる点でしょうか。詳しくはこちらをご参照ください。
mkdir utils utils/auth
touch utils/auth/initFirebase.ts utils/auth/useAuth.ts
mkdir app/store
touch app/store/useStore.ts
initFirebase
import { initializeApp, FirebaseApp, getApp, FirebaseOptions } from 'firebase/app'
export default function initFirebase(config: FirebaseOptions): FirebaseApp {
let app
try {
app = getApp()
} catch (e) {
app = initializeApp(config)
}
return app
}
useAuth.ts
import { useEffect } from "react"
import { getAuth, onAuthStateChanged } from "firebase/auth"
import { useStore } from "../../app/store/useStore"
// オブザーバーを設定してユーザー情報をストアに入れる
export const useAuth = () => {
const { setUser, removeUser } = useStore()
const auth = getAuth()
useEffect(() => {
const cancelAuthListener = onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user)
} else {
removeUser()
}
})
return () => {
cancelAuthListener()
}
}, [])
return null
}
useStore.ts
import create from "zustand"
import { User } from "firebase/auth"
type UserStore = {
user: User | null
setUser: (user: User) => void
removeUser: () => void
}
export const useStore = create<UserStore>((set) => ({
user: null,
setUser: (user: User) => set(() => ({ user: user })),
removeUser: () => set(() => ({ user: null })),
}))
これで一通り準備完了です。ログインできる画面を作りましょう。
touch app/routes/auth.tsx
auth.tsx
import * as React from "react"
import { useEffect, useState } from "react"
import StyledFirebaseAuth from "react-firebaseui/StyledFirebaseAuth"
import { GoogleAuthProvider } from "firebase/auth"
import * as firebaseui from "firebaseui"
import { getAuth } from "firebase/auth"
const firebaseAuthConfig: firebaseui.auth.Config = {
signInFlow: "popup",
signInOptions: [
{
provider: GoogleAuthProvider.PROVIDER_ID,
requireDisplayName: false,
},
],
signInSuccessUrl: "/",
credentialHelper: "none",
callbacks: {
signInSuccessWithAuthResult: ({ user }, redirectUrl) => {
return true
},
},
}
const Index = () => {
const auth = getAuth()
const [renderAuth, setRenderAuth] = useState(false)
useEffect(() => {
if (typeof window !== "undefined") {
setRenderAuth(true)
}
}, [])
return (
<div>
{renderAuth ? (
<StyledFirebaseAuth uiConfig={firebaseAuthConfig} firebaseAuth={auth} />
) : null}
</div>
)
}
export default Index
トップから認証ページに遷移できるようにします。あとログインしてたらid表示するようにしちゃいましょうか。
routes
import { Link } from "remix";
import { useNavigate } from "react-router";
import { useStore } from "../store/useStore";
export default function Index() {
const { user } = useStore();
const navigate = useNavigate();
return (
<div>
<main>
{user && <p>{user.uid}</p>}
<Link to="/auth">to auth</Link>
</main>
</div>
);
}
以上でログイン周辺の実装が完了です。
Tailwind導入
続けてもう一つの目的であるTailwindを導入しましょう。
といっても正直こちらの記事がめっちゃいいのでこちらを参考にしてください。一点、package.jsonのスクリプトだけdotenvの兼ね合いで調整が必要なのでご留意ください。
一通り導入が済んだら、package.jsonを開きます。scriptsのうち、devを調整しましょう。
package.json
"scripts": {
"dev": "node -r dotenv/config node_modules/.bin/remix dev & concurrently \"npm run dev:css\"",
これで環境変数を読みつつTailwindも適用することができます。
というわけで最後はあまり書くことがなかったんですが、一通り開発の準備を整えることができました。
Discussion