🚀

jotaiとumijs,firebase authでログイン

2024/07/11に公開

忘れないうちにメモします。

jotaiとは?

https://jotai.org/

とても使いやすい状態管理ライブラリです。

umijsとは?

https://umijs.org/

nextのようなライブラリです。
next使ったことないですが、おそらく似たような機能を提供していると思われます。
こちらのライブラリの良いところはant designというUIフレームワークとの親和性が高いところです。
私が基本的にant designを使用しているため、こちらを使用しています。

ドキュメントが中国語なのでとっつきにくいですが、cursorで読み込ませたら英語も中国語も一緒なので、あまり関係なく使用できます。

コード

currentUserを監視するauthguard.tsを適用し、currentUserに値が入った場合にRoutingするようにしています。

firebase-atom.ts

firebase-atom.ts
firebase-atom.ts
import { initializeApp } from 'firebase/app';
import { Auth, User, getAuth, onAuthStateChanged } from 'firebase/auth';
import { atom, getDefaultStore } from 'jotai';
import {firebaseConfig} from 'firebase-config';

const firebaseAppAtom = atom(initializeApp(firebaseConfig));

const authAtom = atom<Auth>((get) => {
  const app = get(firebaseAppAtom);
  return getAuth(app);
});

const currentUserAtom = atom<User | null>(null);

currentUserAtom.onMount = (set) => {
  const auth = getDefaultStore().get(authAtom);
  onAuthStateChanged(auth, (user) => {
    set(user);
  });
};

export { authAtom, currentUserAtom, firebaseAppAtom };

こちらではfirebaseをimplementしていきます。

currentUserAtomがマウントされた際にauthを監視していくように書いています。

auth-guard.tsx

auth-guard.tsx
auth-guard.tsx
import { useAtom } from 'jotai';
import { Navigate, Outlet } from 'umi';
import { currentUserAtom } from './firebase-atom';

export default (props: any): JSX.Element => {
  return (
    <AuthGuard>
      <Outlet />
    </AuthGuard>
  );
};

const AuthGuard = ({ children }: { children: React.ReactNode }) => {
  const [user] = useAtom(currentUserAtom);

  if (user) {
    return <Outlet />;
  } else {
    return <Navigate to="/signin" />;
  }
};

authguardです。currentUserを監視してその値がnullでないときに画面を出しわけるように書いています。

signin-page.tsx

signin-page.tsx
sigin-page.tsx
import { useNavigate } from '@/.umi/exports';
import { authAtom, currentUserAtom } from '@/wrappers/firebase-atom';
import { GoogleOutlined } from '@ant-design/icons';
import { Button } from 'antd';
import { Auth, GoogleAuthProvider, signInWithPopup } from 'firebase/auth';
import { useAtom } from 'jotai';
import { useEffect } from 'react';

export const signOut = (auth: Auth) =>
  auth.signOut().then(() => console.log('signed out'));
export const signIn = async (auth: Auth) => {
  const provider = new GoogleAuthProvider();

  const credential = await signInWithPopup(auth, provider);
};

const LoginScreen = () => {
  const [auth] = useAtom(authAtom);
  const [user] = useAtom(currentUserAtom);
  const navigate = useNavigate();
  useEffect(() => {
    if (user) {
      navigate('/');
    }
  }, [user]);
  return (
    <div
      style={{
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        height: '100vh',
      }}
    >
      <Button
        type="primary"
        icon={<GoogleOutlined />}
        size="large"
        onClick={() => signIn(auth)}
      >
        Googleでサインイン
      </Button>
    </div>
  );
};

export default LoginScreen;

こちらではログイン処理をおこなっています。
Google SignInのみです。
signInに関しては既にcurrentUserを監視しているため、画面遷移は記述しません。
useEffectcurrentUserが見つかったときはルート画面に遷移しています。

.umirc.ts

.umirc.ts
.umirc.ts
import { defineConfig } from 'umi';

export default defineConfig({
  routes: [
    {
      path: '/',
      component: 'index',
      wrappers: ['@/wrappers/auth-guard'],
      },
    {
      path: '/signin',
      component: 'signin-page',
    },
    {
      path: '/invoice',
      component: 'invoice-page',
      wrappers: ['@/wrappers/auth-guard'],
    },
  ],
  npmClient: 'pnpm',
});

こちらがミソ。auth-guardはサインイン画面には必要ないので削除します。
その他のページにはauth-guardを設定します。

以上です。

比較的簡単にAuthguardをすることができました。

ご意見などがございましたら是非教えて頂きたいです。

Discussion