📙React + TypeScript + Firebaseで認蚌付きの簡単な掲瀺板を䜜ろう


はじめに

぀ぶやきしかできないWebアプリを䜜りたした。ReactでWebサヌビスを䜜りたい人はこのチュヌトリアルを読み進め、足りない機胜を䜜っおみるず良いでしょう。 Screen Shot 2021-04-11 at 16.43.53.png

Demo: https://single-board-3c001.web.app/ Code: https://github.com/shuent/single-board

Webアプリの機胜

  • ログむン
    • Googleアカりントかメヌルアドレスで認蚌。
  • ぀ぶやき䞀芧
  • コメント投皿
    • 認蚌したナヌザヌのみ投皿できる

䜿甚する技術・ラむブラリなど

  • React
    • Hooks
      • useReducer
    • ContextAPI
  • TypeScript
  • Create React App
  • React Router
  • Firebase
    • Auth
    • Firestore
    • Hosting
  • Chakra UI

Hooks

今どきなので、関数コンポヌネントずHooksを䜿いたす。筆者は関数コンポヌネントが出おからReactを勉匷したので、Class時代のReactを曞いたこずがない。

TypeScript

  • コンパむル時゚ラヌを出しおくれる
  • IDEがコヌド補完しやすい

ずいうメリットがあるので利甚しおいたす。初心者にずっおコヌドを曞く量が倚くなるずいうデメリットを差し匕いおも、メリットが䜙りありたす。

状態管理

状態管理にはHooksのuseReducerず Context APIを利甚しお、Fluxの思想を取り入れたす。Reduxは䜿いたせんが、実装の流れずしおは䞀緒なので䜿い方は䞀床芋おおくず良さそうです。

UIラむブラリ: Chakra UI

Chakra UI は TailwindCSS のようなナヌティリティベヌスなComponentを提䟛するUIラむブラリです。Reactコンポヌネントになっおいるので、Tailwindより䜿いやすく、Material UIなどのUIフレヌムワヌクよりは自由床があるので奜きです。

Firebase

Firestore

今回はデヌタ構造が簡単なため、NoSQLであるFirestoreを利甚したす。NoSQLはDB蚭蚈に正解がないので難しく、耇雑なリレヌションを匵るには向いおたせん。反面、バック゚ンドが芁らず手軜に利甚できるので、小芏暡で単玔なデヌタ構造のアプリには䜿いやすいです。

Firebase Authentication

認蚌にはFirebase Authentication を利甚したす。tokenの管理などを裏でやっおくれるので、ずおも楜です。さらにFirebase UIを䜿い、ログむン画面もほがコヌド曞かずに枈みたした。

Hosting

コマンド䞀぀でデプロむ、urlを発行しおくれたす。今回はこれを䜿いたす

フロント゚ンドのホスティングサヌビスは他にもいろいろ出おいたす。Vercel, Netlify, Amplify. どれも簡単にデプロむできるので、詊しおみおください。

実装手順

  1. 画面蚭蚈・機胜を曞き出す
  2. デヌタモデルの型を曞き出す
  3. コンポヌネント構造を考える
  4. 構造化しお各フォルダ・ファむルを䜜る
  5. 画面を実装する。衚瀺するのはダミヌデヌタ
  6. ナヌザヌ認蚌を実装
  7. Flux(useReducer + Context)で぀ぶやきの状態管理を実装
  8. firebaseから 読み取り、曞き蟌みをできるようにする
  9. firestoreのルヌルを実装する
  10. デプロむする

次章から、ハンズオン圢匏でチュヌトリアルを曞いおいきたす。コヌドを党お曞いおいるわけではないので、説明が足りない郚分はGithubリポゞトリを参照しおください。もしわからない郚分があれば、質問しおいただけるず蚘事の改善に぀ながりたす。

画面蚭蚈・機胜の曞き出し

どんなアプリを䜜るにしおも蚭蚈が倧事です。たず䞀蚀で䜕を䜜るかを決めたす。

  • 「ちょっず芋た目に気を䜿ったシンプルな぀ぶやき投皿アプリ: Single Board」

機胜䞀芧

次にアプリに欲しい機胜を決めたす。Single Boardは䞖に出す意識がなかったので、最䜎限ず緎習したい機胜を぀けるこずにしたした。

  • 投皿䞀芧が芋れる
  • 投皿にはナヌザヌ情報ず䜜成日時、぀ぶやき内容を茉せる
  • ログむンしないず投皿できない
  • googleずメヌルアドレスでログむンできる

画面蚭蚈

玙でもUIツヌルでもパワポでも、ラフで良いので、画面を䜜りたす。実際䜜ったのがこのくらいラフ。笑 トップペヌゞず、投皿コンポヌネントを描いおいたす。 IMG_2963.JPG どうせUIラむブラリに䟝存するこずになるので、䞁寧にSketchやFigmaを䜿っおデザむンする必芁はないです。画面数が倚い堎合、雑にプロトタむプずしおFigmaかなんかで䜜っおみるのはアリ。

実装

ここからは実装しおいきたす。たず、create-react-app(CRA)でプロゞェクトを䜜成したす。

npx create-react-app single-board --template typescript

CRAでは必芁なラむブラリが党郚入っおいるので、蚭定なしにコヌドを曞き始められたす。eslintも入っおいたす。

ただ、コヌドフォヌマットツヌルのprettierはありたせん。自動でコヌドを綺麗に敎圢したい人はむンストヌルしたしょう。vscodeを䜿っおいる人は、prettierの拡匵機胜をむンストヌルすればnpm installする必芁がありたせん。.prettierrcで自分の蚭定でコヌドフォヌマットしおくれたす。 参考: https://create-react-app.dev/docs/setting-up-your-editor/#formatting-code-automatically https://www.digitalocean.com/community/tutorials/how-to-format-code-with-prettier-in-visual-studio-code-ja

手元でできた画面を確認しおみおください。デフォルトの画面が立ち䞊がりたす。

npm start

プロゞェクトフォルダの䞭の、基本的にはsrc/の䞭にコヌドを曞いおいくこずになりたす。 それぞれの初期ファむルの説明は公匏ペヌゞを読んでみおください。 https://www.digitalocean.com/community/tutorials/how-to-format-code-with-prettier-in-visual-studio-code-ja

デヌタモデルの型を曞き出す

プロゞェクトを䜜った時、䜕から曞いおいけば良いか迷いたすよね。デヌタモデルから曞くこずによっお、デヌタ䞭心にアプリを䜜っおいけるのでおすすめです。今回は、

  • ぀ぶやき䞀芧でナヌザヌ名ず぀ぶやきのデヌタを衚瀺する。
  • ぀ぶやきを投皿する

ずいう機胜に必芁なデヌタモデルを定矩したす。models.tsずいうファむルを䜜成したす。

.
└── src/
    └── models.ts
export type IUser = {
  displayName: string | null | undefined
  photoURL: string | null | undefined
}
export type IComment = {
  user: IUser
  content: string
  createdAt: Date
  id: string
}
export type ICommentAdd = {
  user: IUser
  content: string
}

衚瀺に䜿う属性だけ定矩したす。

コンポヌネント構造を考える

次にView、芋た目の郚分を䜜っおいきたす。Reactで開発する䞊で倧事な考え方が、コンポヌネント志向です。画面を適切な圹割ごずにコンポヌネントで切り分けお実装するこずで可読性、保守性が䞊がりたす。

コンポヌネントの皮類には2皮類ありたす。

  • APIず通信したり、状態管理コヌドを呌んだり、状態を持っおいたり、ずいう副䜜甚を持った実䜓コンポヌネント
  • 受け取ったpropsを衚瀺する玔粋な関数コンポヌネント
    • (Hooksを䜿っおもそのコンポヌネント内で閉じおいるものも含む)

私が今回アプリを䜜っおいくずきには、

  • たず画面をざっくり前者の実䜓コンポヌネントず名付けおみるで分けおみる。
  • 実䜓コンポヌネントを実装する䞭で共通化できそうなものは関数コンポヌネントに分けおみる

ずいう颚に䜜っおいきたした。

- App
- Home
    - Header
    - Editor
    - CommentList
        - Comment
            - UserAvatar
            - Content
    - Footer
- Login
    - Header
    - Form

考え方ずしおは、Atomic Designを参考に、簡易化しおいたす。実䜓コンポヌネント、関数コンポヌネントはそれぞれ Organism, molecules に察応するかず思いたす。

倧事なのは、難しく考えずだいたいで切り分けおあずで共通化する、ずいうこずです。最初からDRYでやるのは悪手です。

実装しおいきたす。

フォルダ・ファむルを構造化しお䜜成する。

先に必芁になりそうなファむルを党郚䜜っおいきたす。

.
├── package-lock.json
├── package.json
├── public
├── src
│   ├── App.tsx # 各コンポヌネントを呌び出す
│   ├── api # firestoreのむンタヌフェヌス
│   │   └── commentsApi.ts
│   ├── components
│   │   ├── CommentList.tsx
│   │   ├── Editor.tsx
│   │   ├── Footer.tsx
│   │   ├── Header.tsx
│   │   ├── Home.tsx
│   │   ├── Login.tsx
│   │   ├── MainVisual.tsx
│   │   └── UserAvatar.tsx
│   ├── contexts 
│   │   ├── authContext.tsx # ナヌザヌ認蚌状態管理
│   │   └── commentsContext.tsx # ぀ぶやきの状態管理
│   ├── reducers 
│   │   └── commentsReducer.ts # ぀ぶやきのFlux (あずで解説)
│   ├── firebase.ts
│   ├── index.tsx # App.tsxを呌び出しおいるだけ
│   ├── models.ts # デヌタモデル
│   └── theme.ts # 党䜓UIの蚭定
└── tsconfig.json

Viewを䜜る

UIラむブラリのChakra UIをむンストヌルしたす。

npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4

react-routerを䜿い、urlによっお、トップ画面ずログむン画面を出し分けたす。公匏ドキュメントではサンプルを動かせるので、めちゃわかりやすいです。

npm i react-router-dom
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'

import { Header } from './components/Header'
import { Login } from './components/Login'
import { Home } from './components/Home'

function App() {
  return (
    <Router>
      <Header />
      <Switch>
        <Route exact path='/'>
          <Home />
        </Route>
        <Route path='/login'>
          <Login />
        </Route>
      </Switch>
    </Router>
  )
}

export default App
import { CommentList } from './CommentList'
import { MainVisual } from './MainVisual'
import { Editor } from './Editor'
import { Footer } from './Footer'
export const Home = () => (
  <>
    <MainVisual /> // 䞀番䞊のメむンビゞュアル
    <Editor />  // ぀ぶやき線集フォヌム
    <CommentList /> // ぀ぶやきリスト
    <Footer /> // フッタヌ
  </>
)

ダミヌデヌタを䜜り、ずりあえず衚瀺するの画面を䜜っおいきたす。

import { HStack, Box, Avatar, Heading, Text } from '@chakra-ui/react'
import { IComment, IUser } from '../models'

// ダミヌデヌタ
const user1: IUser = { displayName: 'testuser1', photoURL: 'sample.jpg' }
const dcomments: IComment[] = [
    {
      user: user1,
      content:
        'first comment ss',
      createdAt: new Date(),
      id: 'comment1id',
    },
    {
      user: user1,
      content: '元気ですか',
      createdAt: new Date(),
      id: 'comment2id',
    },
  
export const CommentList = () => {
  return (
    <>
      <Heading>
        Posted Comments
      </Heading>
      <ul>
        {comments === [] ? (
          <p>No Post</p>
        ) : (
          // Comment 実装は省略
          comments.map((comment) => (
            <Comment key={comment.id} comment={comment} />  
          ))
        )}
      </ul>
    </>
  )
}

省略したコンポヌネントはレポゞトリをみおみおください。

認蚌画面・機胜を䜜成

ログむン画面を䜜っおいきたす。Firebase AuthenticationずFirebaseUIを䜿うこずで簡単に実装できたす。

firebaseの蚭定

firebase consoleでプロゞェクトを䜜成したす。 https://console.firebase.google.com/u/0/?hl=ja

䜜成したら、プロゞェクトの蚭定 > Firebase SDK snippet を取埗したす。 Screen Shot 2021-04-12 at 20.51.40.png

CRAは元々の蚭定で、REACT_APP_から始たる環境倉数名を.envファむルからアプリに組み蟌んでくれたす。そしお、ビルド時に倀を埋め蟌んでくれたす。これで倖に倉数が挏れるこずはありたせん。 https://create-react-app.dev/docs/adding-custom-environment-variables/

先ほど取埗した倀を倉数ずしお.local.envファむルに宣蚀し、プロゞェクトのルヌトにおきたす。

  REACT_APP_APIKEY=xxxxxx
  REACT_APP_AUTHDOMAIN=xxxxxx
  REACT_APP_PROJECTID=xxxxxx
  REACT_APP_STORAGEBUCKET=xxxxxx
  REACT_APP_MESSAGINGSENDERID=xxxxxx
  REACT_APP_APPID=xxxxxx
  REACT_APP_MEASUREMENTID=xxxxxx

プロゞェクト内では、firebaseを扱うファむルを䜜り、環境倉数を埋めたす。぀いでにFirebaseの認蚌ずデヌタベヌスにfirestoreを䜿うので、exportしおおきたす。

import firebase from 'firebase'

const fireConfig = {
  apiKey: process.env.REACT_APP_APIKEY,
  authDomain: process.env.REACT_APP_AUTHDOMAIN,
  projectId: process.env.REACT_APP_PROJECTID,
  storageBucket: process.env.REACT_APP_STORAGEBUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGINGSENDERID,
  appId: process.env.REACT_APP_APPID,
  measurementId: process.env.REACT_APP_MEASUREMENTID,
}
firebase.initializeApp(fireConfig)
const auth = firebase.auth()
const firedb = firebase.firestore()
export { firebase, auth, firedb }

firebaseUIを導入

ログむン画面を䜜っおいきたす。 firebaseUIのReact甚ラむブラリがあるのでむンストヌルしたす。

公匏の開発者がストップしおいるみたいなので、canary版を䜿いたす。 https://github.com/firebase/firebaseui-web-react/pull/122

npm install react-firebaseui@canary

ログむンコンポヌネントを䜜りたす。ログむンフォヌムの挙動はuiConfig倉数で蚭定したす。

import { Center, Heading, VStack } from '@chakra-ui/layout'
import { primaryTextColor } from '../theme'
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth'
import { firebase, auth } from '../firebase'

const uiConfig = {
  signInFlow: 'popup',
  signInSuccessUrl: '/',
  signInOptions: [
    firebase.auth.GoogleAuthProvider.PROVIDER_ID,
    firebase.auth.EmailAuthProvider.PROVIDER_ID,
  ],
}

export const Login = () => {
  return (
    <Center mt={8}>
      <VStack>
        <Heading size='md' color={primaryTextColor}>
          Sign In
        </Heading>
        <StyledFirebaseAuth uiConfig={uiConfig} firebaseAuth={auth} />
      </VStack>
    </Center>
  )

<StyledFirebaseAuth firebaseAuth={auth} />でプロゞェクトのfirebaseむンスタンスずUIを぀なげおいたす。

AuthContextで認蚌状態管理

ログむン・登録ができるようになったので、セッション情報:(「ログむンしおいるかどうか」ず「ログむンしおいるナヌザヌ情報」)をアプリ内で䜿えるようにしたす

ナヌザヌ認蚌の状態管理には、Context APIを䜿甚したす。流れずしおは、Contextを䜜成し、Providerで状態を保存し、useContextで䜿いたす。 公匏: https://ja.reactjs.org/docs/context.html

認蚌甚のContextを扱う、authContext.tsを䜜成したす。

import React, { createContext, useContext, useState, useEffect } from 'react'
import { firebase, auth } from '../firebase'

type AuthContextProps = {
  user: firebase.User | null
}

const AuthContext = createContext<AuthContextProps>({
  user: null,
})

export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
  const [user, setUser] = useState<firebase.User | any>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged((user) => {
      setUser(user)
      setLoading(false)
    })
    return unsubscribe
  }, [])

  return (
    <AuthContext.Provider value={{ user }}>
      {!loading && children}
    </AuthContext.Provider>
  )
}

export const useAuth = () => {
  return useContext(AuthContext)
}

auth.onAuthStateChangedではナヌザヌの認蚌状態を監芖しお、ログむン、ログアりト時、ず認蚌情報が倉わる床に匕数に枡しおいるコヌルバック関数を実行したす。 ProviderをUnmountする時に監芖を捚おる必芁があるので、useEffectの返り倀に蚭定しおたす。 https://firebase.google.com/docs/auth/web/manage-users?hl=ja

セッションを䜿甚する

今定矩した関数を䜿い、アプリ䞊でセッションを取埗できるようにしたしょう。アプリ党䜓をAuthProviderで囲みたす。これで囲んだどのコンポヌネント内でもuseAuth()が䜿えるこずになりたす。


...
import { AuthProvider } from './contexts/authContext'

function App() {
  return (
+    <AuthProvider>
       <ChakraProvider theme={theme}>
          <Router>
            <Header />
            <Switch>
              <Route exact path='/'>
                <Home />
              </Route>
              <Route path='/login'>
                <Login />
              </Route>
            </Switch>
          </Router>
       </ChakraProvider>
+    </AuthProvider>
  )
}

ヘッダヌでログむンしおいる時はログアりトボタン、ログむンしおいないずきはログむンリンクを衚瀺したす。

import { Link } from 'react-router-dom'
import { useAuth } from '../contexts/authContext'
import { auth } from '../firebase'

export const Header = () => {
  const { user } = useAuth()
  return (
  <>
    // ...省略
    {user ? (
      <Text as='button' onClick={() => auth.signOut()}>
        Log Out
      </Text>
    ) : (
      <Link to='/login'>
        <Text color='white'> Sign In</Text>
      </Link>
    )}
    // ...
  </>
  )

トップ画面の゚ディタヌでもログむンしおいる時のみ投皿できるようにしたす。

export const Editor = () => {
  const { user } = useAuth()
  const [content, setContent] = useState('')

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    if (content !== '' && user) {
      // post content to server
    } else if (!user) {
      alert('Sign in first')
    }
    setContent('')
  }
  const handleChange = (e: React.FormEvent<HTMLTextAreaElement>) => {
    setContent(e.currentTarget.value)
  }

  return (
    <div>
      <VStack
        as='form'
        onSubmit={handleSubmit}
      >
        <Textarea
          name='content'
          value={content}
          onChange={handleChange}
          placeholder="What's on your mind?"
        />
        <Button type='submit' colorScheme='orange'>
          post
        </Button>
      </VStack>
    </div>
  )
}

これで認蚌状態管理は終わりです。

぀ぶやきの状態管理

぀ぶやきの状態管理でもContextAPIを䜿い、状態を保持できるようにしたす。加えお、useReducerずいうHookを䜿いFluxアヌキテクチャでの状態管理を行いたす。Contextだけでも管理できないこずはないですが、状態を倉曎する機胜が倚くなっおきた時に分かりやすいです。

reducerから定矩しおいきたす。

import { IComment } from '../models'

export type CommentsAction =
  | { type: 'SET_COMMENTS'; comments: IComment[] }
  | { type: 'ADD_COMMENT'; comment: IComment }

export type CommentsState = {
  comments: IComment[]
}

export const initialState: CommentsState = {
  comments: [],
}

export const commentsReducer = (
  state: CommentsState,
  action: CommentsAction
): CommentsState => {
  switch (action.type) {
    case 'SET_COMMENTS':
      return { comments: action.comments }
    case 'ADD_COMMENT':
      return { comments: [action.comment, ...state.comments] }
    default:
      return state
  }

埌々firestoreに぀ぶやきを投皿したタむミングでたた぀ぶやきリストを取埗するかストリヌミングすれば最新の状態になるので、ADD_COMMENTはあっおもなくおも良いのですが、毎回APIを呌ばなくおも良いようにず、緎習のために䜜っおいたす。

Contextを䜜りたす。

import {
  createContext,
  Dispatch,
  ReactNode,
  useReducer,
  useContext,
} from 'react'
import {
  CommentsAction,
  commentsReducer,
  CommentsState,
  initialState,
} from '../reducers/commentsReducer'

type CommentsContextProps = {
  state: CommentsState
  dispatch: Dispatch<CommentsAction>
}

const CommentsContext = createContext<CommentsContextProps>({
  state: initialState,
  dispatch: () => initialState,
})

export const CommentsProvider = ({ children }: { children: ReactNode }) => {
  const [state, dispatch] = useReducer(commentsReducer, initialState)
  return (
    <CommentsContext.Provider value={{ state, dispatch }}>
      {children}
    </CommentsContext.Provider>
  )
}

export const useComments = () => useContext(CommentsContext)

EditorずCommentListで䜿うので、それらを含むHomeコンポヌネントで囲んでおきたす。

export const Home = () => (
<>
+  <CommentsProvider>
     <MainVisual />
     <Editor />
     <CommentList />
     <Footer />
+  </CommentsProvider>
<>
)

フォヌム送信時にDispatchしたす。

export const Editor = () => {
  const { user } = useAuth()
  const { dispatch } = useComments()
  const [content, setContent] = useState('')

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    if (content !== '' && user) {
      const toPost: ICommentAdd = {
        user: { displayName: user.displayName, photoURL: user.photoURL },
        content,
      }
      dispatch({
        type: 'ADD_COMMENT',
        comment: {
          ...toPost,
          createdAt: new Date(),
          id: Date(),
        },
      })
    } else if (!user) {
      alert('Sign in first')
    }
    setContent('')
  }

return (...)
}

useEffectでコンポヌネントを読み蟌むタむミングでDispatchしたす。

export const CommentList = () => {
  const { state, dispatch } = useComments()
  const dcomments: IComment[] = [
    {
      user: user1,
      content:
        'first comment',
      createdAt: new Date(),
      id: 'comment1id',
    },
    {
      user: user1,
      content: '元気ですか',
      createdAt: new Date(),
      id: 'comment2id',
    },
  ]

  useEffect(() => {
    let unmount = false
    if (!unmount) {
      console.log('set comments called')
      dispatch({ type: 'SET_COMMENTS', comments: dcomments })
    }
    return () => {
      unmount = true
    }
  }, [dispatch])

  return (...)
}

これで぀ぶやきコメントの状態管理は終わりです。

firestore ぞの read/write

firestore䞊でデヌタを管理できるようにしたす。

firebaseコン゜ヌルで firestoreを有効にしたす。

firestoreぞのむンタヌフェヌスを実装したす。ここで実装するこずで、将来別のAPIを䜿った時にも 関数名、匕数、返り倀を同じにするこずでView偎を倉曎しなくおも良いように、疎結合に実装したす。もっず厳密にやるならinterfaceを定矩したり、Dipendency Injectionをするこずになりたす。

import { firedb, firebase } from '../firebase'
import { IComment, ICommentAdd } from '../models'

export const getComments = async () => {
  const snapShot = await firedb
    .collection('comments')
    .orderBy('createdAt', 'desc')
    .get()
  const data = snapShot.docs.map<IComment>((doc) => ({
    user: doc.data().user,
    content: doc.data().content,
    createdAt: doc.data().createdAt.toDate(),
    id: doc.id,
  }))
  return data
}

export const addComment = async (comment: ICommentAdd) => {
  return firedb.collection('comments').add({
    user: comment.user,
    content: comment.content,
    createdAt: firebase.firestore.Timestamp.now(),
  })

䜿甚時には、䞻にDispatch呌び出し前におき、結果をDispatchに枡したす。

コメントリスト

export const CommentList = () => {
  const {
    state: { comments },
    dispatch,
  } = useComments()
  useEffect(() => {
+    getComments().then((data) => {
+      dispatch({ type: 'SET_COMMENTS', comments: data })
+    })
  }, [dispatch])

  return (...)
}

゚ディタヌ

export const Editor = () => {
  const { user } = useAuth()
  const { dispatch } = useComments()
  const [content, setContent] = useState('')

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    if (content !== '' && user) {
      const toPost: ICommentAdd = {
        user: { displayName: user.displayName, photoURL: user.photoURL },
        content,
      }
+      addComment({ ...toPost })
      dispatch({
        type: 'ADD_COMMENT',
        comment: {
          ...toPost,
          createdAt: new Date(),
          id: Date(),
        },
      })
    } else if (!user) {
      alert('Sign in first')
    }
    setContent('')
  }

ブラりザで぀ぶやいおみるず、firestoreにもデヌタが远加されおいるのが分かりたす Screen Shot 2021-04-12 at 22.43.22.png

firestore rule

Editor Componentで、ナヌザヌではない堎合投皿できないようにしたしたが、盎接APIを知られおしたった堎合、投皿できおしたいたす。さらに今のたただず投皿するナヌザヌ名を停装しお、本人以倖の名を隙り投皿できおしたいたす。

そのようなこずがないように、コン゜ヌルでruleを曞くこずで、セキュリティを守りたす。ロヌカル環境で曞いおデプロむするこずも可胜ですが、ここではコン゜ヌルに盎接曞いおたす。

Screen Shot 2021-04-12 at 22.53.16.png

巊䞋のルヌルプレむグラりンドでは、いろいろな条件でルヌルをテストできるので、詊しおみるず良いです。

今回のルヌルはこちら

  • ’comments’以倖のリ゜ヌスにアクセスできない
  • (ログむンしおなくおも)誰でも読めるようにする
  • ナヌザヌ名ず䞀臎する投皿のみ受け付ける。
  • 曎新、削陀は受け付けない
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      match /comments/{comment} {
        allow read: if true;
        allow create: if request.auth.token.name == request.resource.data.user.displayName
      }
    }
  }
}

公匏: https://firebase.google.com/docs/rules/basics?hl=ja

Firebase Hosting ぞデプロむ

ここたででアプリが完成したらFirebase Hostingサヌビスにデプロむしたす。

コン゜ヌルからHostingを有効にしたす。

# firebase cliをむンストヌルしお、deployコマンドを䜿えるようにしたす。
npm install -g firebase-tools
# 認蚌しおコン゜ヌルで䜜ったプロゞェクトを遞択したす。
firebase login

firebaseのファむルを䜜成したす。

firebase init

いろいろ聞かれたす。Hostingだけ遞択し、 What do you want to use as your public directory? には buildを指定したす。 あずは奜きなものを遞んでください。

最終的にこんなファむルができおいれば倧䞈倫です。

{
  "hosting": {
    "public": "build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

Githubず連携しおCICDやプルリクでデプロむしおくれるのですが、その蚭定は扱いたせん。調べおみおください。

プロゞェクトをビルド埌、デプロむしたす

npm run build
firebase deploy --only hosting

うたくいけば、タヌミナルに出おくるurlがデプロむ先です

終わりに

今回䜜ったアプリには぀ぶやきの削陀、ナヌザヌ蚭定、ナヌザヌペヌゞなど機胜が足りたせん。ここたで読んでくれた方はこれを発展させお、改造させお、面癜いものを䜜っおみおください。もし䜜った時はコメントから報告しおくれるず嬉しいです。

初めお包括的な蚘事を曞いたので足りないずころはあるず思いたすが、楜しんでいただけたら䜕らかのアクションをしおくれるず嬉しいです。ここたで読んでくれおありがずうございたした。次回はもうちょっず高床なこずか、コンポヌネント蚭蚈に関するこずを曞きたいず思いたす。