バックエンドはRuby on Railsを主に使っていますがフロントエンドはフレームワークをあまりつかっていないことに気づいたので試してみる
next 9.5.5 react 17.0.1 recoil ^0.0.13 typescript ^4.0.5 firebase ^8.0.0
環境構築
Next.jsには Rails new のように雛形となるプロジェクトをコマンド経由で作成できるので、雛形となるプロジェクトを作成していきます。 yarnを使っていきます。
$ yarn create next-app プロジェクト名
$ yarn create next-app mofmof-box
今回はmofmof-boxという名前で作っていきましょう。
作成が終わったらvscodeでプロジェクトを開きます
$ code mofmof-box
このタイミングで一度正しくプロジェクトを作成できているかを確認しておきます。
$ yarn dev

アクセスして上記のような画面が表示されればオッケーです。デフォルトはlocalhost:3000となっています。
TypeScriptを導入する
Next.jsはコマンドで生成した段階ではJavaScriptで記載されています。 このままjsで開発してもいいですが型の恩恵をうけておきたいです。 無駄なエラーも防ぎたいのでTypeScriptを導入しましょう。
tsconfig.jsonを作成する
プロジェクト直下にtsconfig.jsonを作成します。その状態で yarn devを行うとコンソールにエラーが出ます。
typescriptを使いたい場合は下記コマンドを打てと言ってくるので、それに従います。
next-env.d.tsというファイルが作成されれば成功です。
また、create時に生成されたファイル群の中に.js拡張子のものがいくつかできてしまっているので、それは.tsxにしておきましょう。
諸々終わったらyarn devし直してもう一度起動します。エラーが消えてればオッケーです。
Firebaseでユーザーをいい感じに認証させる
ユーザー認証にはみんな大好きfirebaseを使います。今回はvercelを使うので、hostingのチェックは外して作りましょう。もちろんwebです
一通りの登録ができたら、Next.jsでfirebaseを使えるようにします。
$ yarn add firebase
設定ファイルを作成します。
lib/firebase.ts
import { firebase } from '@firebase/app'
import '@firebase/auth'
import '@firebase/firestore'
if( process.browser && firebase.apps.length === 0){
const config = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
}
firebase.initializeApp(config)
}
NEXT_PUBLIC_というプレフィックスをつけるとブラウザでもその環境変数が利用できるとのことなので、大人しく従っておきましょう。
Next.jsにはデフォルトでdotenvライブラリが入っているので.env.localにでも上記の環境変数を入れておきます。
設定できたら、最後に_app.tsxにfirebase.tsをインポートしておきましょう。
ユーザー認証
FirebaseAuthenticationを利用してユーザー認証を行う。
今回はメール認証も書くのちょっとだるいのでFirebaseAuthenticationはメインではないので匿名認証を使ってぱぱっと作っていく。どこかで書き換えやるかも。もしメール認証がしたかったらそっちを有効にしていきます。
SNS認証も提供してくれているが、SNS側にも色々設定を書いたりしないといけないのでここでは割愛。
hooks/auth.tsというファイルを作成して書いてみる
import { firebase } from '@firebase/app'
export const useAuthenticate = () => {
firebase
.auth()
.signInAnonymously()
.catch(function (e) {
console.log(e.message)
})
firebase.auth().onAuthStateChanged(function (u) {
if (u) {
console.log(u.uid)
console.log(u.isAnonymous)
} else {
// signed outの時の処理
}
})
}
if (process.browser) {
authenticate()
}
こんな感じ。 process.browserでブラウザが開いてる時に認証を呼ぶ。もしemailでの認証ならsignInAnnonymously()の部分をcreateUserWithEmailAndPassword(email, pass)とすれば良いです。
ブラウザで検証ツールを用いて調べると
console.log(u.uid) console.log(u.isAnonymous)
の記載によってuidとtrue(今回は匿名ログインなので)が帰ってくる。 確認できれば匿名ログインは成功している。
Recoilを使って状態保持を行う
今Reactを使っているプロジェクトはGraphQLを使用して状態管理を任せていますが、RESTの場合はRedux等を使って状態管理を行う。 とはいえ大きくない規模にReduxはオーバーな感じもあるので、今回はFB製のライブラリであるRecoilを使って状態を管理してみる。
ライブラリをインストール
$ yarn add recoil
使い方はRecoilを使うコンポーネントを
pages/_app.tsx
import { RecoilRoot } from 'recoil'
import '../styles/globals.css'
import '../lib/firebase'
import '../hooks/auth'
function MyApp({ Component, pageProps }) {
return (
<RecoilRoot>
<Component {...pageProps} />
</RecoilRoot>
)
}
export default MyApp
Recoilにはatomという機能が提供されているらしい。 atomはstateの一部を表す事ができ、任意のコンポーネントから読み書きできる。atomの値を読み込むコンポーネントは暗黙の内にそのアトムをサブスクライブしているので、該当アトムを更新するとそのアトムをサブスクライブしているすべてのコンポーネントが再レンダリングされる。
みたいなことが公式に書いてあります。良い感じです。
公式の例を参考に、atomを実装してみる。
type User = {
uid: string,
isAnonymous: boolean
}
const userState = atom<User>({
key: 'user',
default: null
})
keyは他の名前とかぶらないようなユニークな名前をつけます。この名前で読み書き対象を指定できる。
defaultはデフォルト値。 この2つがatomオプションで必須の項目になるとのこと。
atomが指定できたら、Recoilでuserを管理できるようにauthenticate関数を少し変更していきます。
import { useEffect } from 'react'
import { firebase } from '@firebase/app'
import { atom, useRecoilState } from 'recoil'
type User = {
uid: string,
isAnonymous: boolean
}
const userState = atom<User>({
key: 'userState',
default: null
})
export const useAuthenticate = () => {
const [user, setUser] = useRecoilState(userState)
useEffect(() => {
if (user) return
firebase
.auth()
.signInAnonymously()
.catch((e) => {
console.log(e.message)
})
firebase.auth().onAuthStateChanged((u) => {
if (u) {
console.log(u)
setUser({uid: u.uid, isAnonymous: u.isAnonymous})
} else {
setUser(null)
}
})
}, [])
return user
}
authenticateメソッドは他の場所で使用できるようにuserを返すようにしておく。また、今回はブラウザを開いたときに匿名ログインを行うので、最初に一度だけ行うことにする。
index.tsxに
import { useAuthenticate } from '../hooks/auth';
を行い、コンポーネントの中で
const user = useAuthenticate()
として、importしたauthenticate関数の実行結果をuserに入れておきます。
<div>{user.uid)</div>
返すコンポーネントの中に上記のようなuidを表示するものを入れてみます。

どこでもいいですが今回はページ下部に入れてみました。画像のように自分が要素を足した部分にuidが表示されていればオッケーです。
次回はデータベースをfirestoreで作ってデータを永続化したりしてみる