- React アプリから Firebase Realtime Database を利用
1-1. Firebase GUI にてアプリの登録
1-2. Firebase GUI にて Realtime Database の設定
1-3. Firebase GUI にて Hosting の設定
1-4. React にて Firebase Realtime Database と連携し簡単なチャットアプリの実装 - 1.で作成したアプリを Firebase Hosting にデプロイ
2-1. GitHub Actions から Firebase Hosting にデプロイ
2-2. Firebase Cli から Firebase Hosting にデプロイ
-
Firebase:を利用するため Gooble アカウントを所有していること
-
React アプリが構築できること(参考 node.js,npm,yarn のバージョン)
$ node -v v16.2.0 $ npm -v 7.19.1 $ yarn -v 1.22.10 $ npx -v 7.19.1
今回利用する Firebase の API キーについては公式のドキュメントを一読推奨
Learn about using and managing API keys for Firebase
Firebaseを開き「コンソールへ移動」を押下
「プロジェクトを追加」を押下
「プロジェクト名」を入力し「続行」を押下
「アナリティクス」を無効(今回はサンプルコードのため)にし「プロジェクトを作成」を押下
「続行」を押下
「Realtime Database」を押下
「データベースを作成」を押下
「米国」を選択し「次へ」を押下
「テストモードで開始」を選択し「有効にする」を押下
作成されたことを確認
「プロジェクトの概要」を押下し、「</>」(Web)を押下
「アプリのニックネーム」を記載し「アプリを登録」を押下 (Firebase Hosting の設定はチェックしない)
スクリプトをクリップボードにコピーし「コンソールに進む」を押下(npm install firebase
はreact
の環境を構築後に行う)
一時的なファイルを作成しクリップボードにコピーした内容をペーストする
任意のディレクトリ(プロジェクト名と合わせる場合はfirebase-react-sample
)を作成し遷移後create-react-app
で React ベースアプリを作成する
$ mkdir firebase-react-sample
$ cd firebase-react-sample
$ yarn create react-app .
注意:yarn
が使えない場合はnpx create-react-app .
で React APP を作成。作成後README.md
のガイダンスがnpm
の場合は以降yarn
を全てnpm
に読み替えて行うこと
一時的に退避したファイルをプロジェクト直下に.env.local
として作成しクリップボードにコピーした内容をペーストする(除外指定されていない場合は後ほど.gitignore に指定すること)
localhost:3000
で起動すること
$ yarn start
src/components
ディレクトを作成する
App.js
をsrc/components
に移動し以下に変更する
import React from "react"
const App = () => {
return <div>hello</div>
}
export default App
index.js
を import しているApp
をcomponents/App
にパスを修正しその他以下に変更する
import React from "react"
import ReactDOM from "react-dom"
import App from "./components/App"
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
)
ブラウザをリロードしhello
が表示されていれば修正は成功
確認
$ yarn info firebase
インストール
$ yarn add firebase
yarn start
は Ctrl+C で停止し改めてスタートする
$ yarn start
事前に作成した.env.local
をサンプル .evn.local
のフォーマットに修正する
REACT_APP_FIREBASE_API_KEY=''
REACT_APP_FIREBASE_AUTH_DOMAIN=''
REACT_APP_FIREBASE_DATABASE_URL=''
REACT_APP_FIREBASE_PROJECT_ID=''
REACT_APP_FIREBASE_STORAGE_BUCKET=''
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=''
REACT_APP_FIREBASE_APP_ID=''
src
直下にfirebase.js
として作成
import firebase from 'firebase/compat/app'
import 'firebase/compat/database'
const {
REACT_APP_FIREBASE_API_KEY,
REACT_APP_FIREBASE_AUTH_DOMAIN,
REACT_APP_FIREBASE_DATABASE_URL,
REACT_APP_FIREBASE_PROJECT_ID,
REACT_APP_FIREBASE_STORAGE_BUCKET,
REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
REACT_APP_FIREBASE_APP_ID,
} = process.env
const firebaseConfig = {
apiKey: REACT_APP_FIREBASE_API_KEY,
authDomain: REACT_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: REACT_APP_FIREBASE_DATABASE_URL,
projectId: REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
appId: REACT_APP_FIREBASE_APP_ID,
}
firebase.initializeApp(firebaseConfig)
const database = firebase.database()
export const messagesRef = database.ref('messages')
export const pushMessage = ({ name, text }) => {
messagesRef.push({ name, text })
}
.env.local
を反映させるため、Ctrl+C で停止し改めてyarn start
でスタートする
$ yarn start
App.js
を以下に修正(空文字対応、エンター受付(全角、半角)、フォーカス固定などは行っていない)
import { useState } from "react"
import { pushMessage } from "../firebase"
const App = () => {
const [name, setName] = useState("default")
const [text, setText] = useState("text")
return (
<>
<input
type="text"
value={name}
onChange={(e) => setName((name) => (name = e.target.value))}
/>
<input
type="text"
value={text}
onChange={(e) => setText((text) => (text = e.target.value))}
/>
<button onClick={() => pushMessage({ name: name, text: text })}>
push
</button>
</>
)
}
export default App
別な書き方
import { useState } from 'react'
import { pushMessage } from '../firebase'
const App = () => {
const [data, setData] = useState({ name: 'default', text: 'text' })
const setNameFunc = (e) => {
setData((prevData) => ({ ...prevData, name: e.target.value }))
}
const setTextFunc = (e) => {
setData((prevData) => ({ ...prevData, text: e.target.value }))
}
const pushMessageToFirebase = () => {
pushMessage({ ...data })
}
return (
<>
<input type="text" value={data.name} onChange={(e) => setNameFunc(e)} />
<input type="text" value={data.text} onChange={(e) => setTextFunc(e)} />
<button onClick={() => pushMessageToFirebase()}>push</button>
</>
)
}
export default App
push button を押下し Realtime Database にデータが反映されていることを確認する
App.js
を以下に修正
import { useState, useEffect } from "react"
import { messagesRef, pushMessage } from "../firebase"
const App = () => {
const [name, setName] = useState("default")
const [text, setText] = useState("text")
const [messages, setMessages] = useState([])
useEffect(() => {
messagesRef
.orderByKey()
.limitToLast(10)
.on("value", (snapshot) => {
const messages = snapshot.val()
if (messages === null) return
const entries = Object.entries(messages)
const newMessages = entries.map((data) => {
const [key, message] = data
return { key, ...message }
})
setMessages(newMessages)
})
}, [])
return (
<>
{messages.map((message) => (
<div key={message.key}>
{message.name}:{message.text}
</div>
))}
<input
type="text"
value={name}
onChange={(e) => setName((name) => (name = e.target.value))}
/>
<input
type="text"
value={text}
onChange={(e) => setText((text) => (text = e.target.value))}
/>
<button onClick={() => pushMessage({ name: name, text: text })}>
push
</button>
</>
)
}
export default App
別な書き方
import { useState, useEffect } from 'react'
import { messagesRef, pushMessage } from '../firebase'
import Message from './Message'
const App = () => {
const [data, setData] = useState({ name: 'default', text: 'text' })
const [messages, setMessages] = useState([])
const setNameFunc = (e) => {
setData((prevData) => ({ ...prevData, name: e.target.value }))
}
const setTextFunc = (e) => {
setData((prevData) => ({ ...prevData, text: e.target.value }))
}
const pushMessageToFirebase = () => {
pushMessage({ ...data })
}
const setMessageFunc = (newMessage) => {
setMessages((prevNewMessages) => (prevNewMessages = [...newMessage]))
}
useEffect(() => {
messagesRef
.orderByKey()
.limitToLast(3)
.on('value', (snapshot) => {
const messages = snapshot.val()
if (!messages) return
const entries = Object.entries(messages)
const newMessage = entries.map((data) => {
const [key, message] = data
return { key, ...message }
})
setMessageFunc(newMessage)
})
}, [])
return (
<>
{messages.map((message) => (
<Message key={message.key} message={message} />
))}
<input type="text" value={data.name} onChange={(e) => setNameFunc(e)} />
<input type="text" value={data.text} onChange={(e) => setTextFunc(e)} />
<button onClick={() => pushMessageToFirebase()}>push</button>
</>
)
}
export default App
./src/components/Message.js
import { memo } from 'react'
const Message = memo(({ message }) => {
return (
<div>
{message.name}:{message.text}
</div>
)
})
export default Message
更に別な書き方(Custom Hooks + memo + useCallback)
./src/components/App.js
import { memo } from 'react'
import Message from './Message'
import useApp from '../hooks/useApp'
const App = memo(() => {
const { setNameFunc, setTextFunc, pushMessageToFirebase, messages, data } =
useApp()
return (
<>
{messages.map((message) => (
<Message key={message.key} message={message} />
))}
<input type="text" value={data.name} onChange={(e) => setNameFunc(e)} />
<input type="text" value={data.text} onChange={(e) => setTextFunc(e)} />
<button onClick={() => pushMessageToFirebase()}>push</button>
</>
)
})
export default App
./src/components/Message.js
(変更なし)
import { memo } from 'react'
const Message = memo(({ message }) => {
return (
<div>
{message.name}:{message.text}
</div>
)
})
export default Message
.src/hooks/useApp.js
import { useState, useEffect, useCallback } from 'react'
import { messagesRef, pushMessage } from '../firebase'
const useApp = () => {
const [data, setData] = useState({ name: 'default', text: 'text' })
const [messages, setMessages] = useState([])
const setNameFunc = useCallback(
(e) => {
setData((prevData) => ({ ...prevData, name: e.target.value }))
},
[setData]
)
const setTextFunc = useCallback(
(e) => {
setData((prevData) => ({ ...prevData, text: e.target.value }))
},
[setData]
)
const pushMessageToFirebase = useCallback(() => {
pushMessage({ ...data })
}, [data])
const setMessageFunc = useCallback((newMessage) => {
setMessages((prevNewMessages) => (prevNewMessages = [...newMessage]))
}, [])
useEffect(() => {
messagesRef
.orderByKey()
.limitToLast(3)
.on('value', (snapshot) => {
const messages = snapshot.val()
if (!messages) return
const entries = Object.entries(messages)
const newMessage = entries.map((data) => {
const [key, message] = data
return { key, ...message }
})
setMessageFunc(newMessage)
})
}, [setMessageFunc])
return { setNameFunc, setTextFunc, pushMessageToFirebase, messages, data }
}
export default useApp
Chrome DevTools を開きSources
->localhost:3000
を右クリックし API キー(REACT_APP_FIREBASE_API_KEY
)で検索してみましょう
$ yarn build
✨ Done in 10.65s.
build
ディクレトリが作成されていることを確認(ll
はls -la
のalias
)
$ ll build
total 80
drwxr-xr-x 10 h-miura staff 320 2 18 16:13 .
drwxr-xr-x 15 h-miura staff 480 2 18 16:21 ..
-rw-r--r-- 1 h-miura staff 683 2 18 16:13 asset-manifest.json
-rw-r--r-- 1 h-miura staff 3870 2 18 16:13 favicon.ico
-rw-r--r-- 1 h-miura staff 2143 2 18 16:13 index.html
-rw-r--r-- 1 h-miura staff 5347 2 18 16:13 logo192.png
-rw-r--r-- 1 h-miura staff 9664 2 18 16:13 logo512.png
-rw-r--r-- 1 h-miura staff 492 2 18 16:13 manifest.json
-rw-r--r-- 1 h-miura staff 67 2 18 16:13 robots.txt
drwxr-xr-x 3 h-miura staff 96 2 18 16:13 static
build
ディレクトリに遷移しhttp-server
でアプリを起動しブラウザでhttp://127.0.0.1:8080
を開き動作確認を行う。(停止は CTRL+C)
$ cd build
$ npx http-server
npx: 23個のパッケージを2.34秒でインストールしました。
Starting up http-server, serving ./
Available on:
http://127.0.0.1:8080
http://192.168.1.12:8080
http://192.168.1.14:8080
Hit CTRL-C to stop the server
CTRL-C で停止しアプリのホームディレクトリに戻る
$ cd ..
(この章は GitHub で管理、デプロイする際に必要な手順です、GitHub にて管理やデプロイしない場合は不要です)
GitHub にて今回用のリポジトリを作成するNew repository
を押下
Repository name
を記入しCreate repository
を押下
利用するプロトコルの選択し下部赤枠をコピーし実行する
git remote add origin [email protected]:hironomiu/firebase-react-sample.git
git branch -M main
git push -u origin main
今回のアプリケーションを GitHub に push する
git add .
git commit -m "commited"
git push origin main
「Hosting」を押下
「始める」を押下
ローカルインストール
$ yarn add --dev firebase-tools
確認
$ npx firebase -V
10.1.0
「次へ」を押下
ローカルインストールのためnpx
をつけ実行する
$ npx firebase login
Already logged in as [email protected]
矢印などで上下させ「Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys」をを選択しスペースを押下しエンターを押下
$ npx firebase init
######## #### ######## ######## ######## ### ###### ######## ## ## ## ## ## ## ## ## ## ## ## ###### ## ######## ###### ######## ######### ###### ###### ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ######## ######## ## ## ###### ########
You're about to initialize a Firebase project in this directory:
/Users/Desktop/test
? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm
your choices.
◯ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance
◯ Firestore: Configure security rules and indexes files for Firestore
◯ Functions: Configure a Cloud Functions directory and its files
❯◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
◯ Hosting: Set up GitHub Action deploys
◯ Storage: Configure a security rules file for Cloud Storage
◯ Emulators: Set up local emulators for Firebase products
「Use an existing project」を選択しエンターを押下
=== Project Setup
First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.
? Please select an option: (Use arrow keys)
❯ Use an existing project
Create a new project
Add Firebase to an existing Google Cloud Platform project
Don't set up a default project
今回作成したプロジェクトを選択しエンターを押下
=== Project Setup
First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.
? Please select an option: Use an existing project
? Select a default Firebase project for this directory: (Use arrow keys)
❯ fir-react-sample (firebase-react-sample)
xxxxxx (xxxxxx)
yyyyyy (yyyyyy)
build
を入力しエンターを押下
=== Hosting Setup
Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.
? What do you want to use as your public directory? build
「y」を押下
? Configure as a single-page app (rewrite all urls to /index.html)? Yes
「y」を押下(GitHub)
? Set up automatic builds and deploys with GitHub? Yes
「n」を押下
? File build/index.html already exists. Overwrite? No
i Skipping write of build/index.html
i Writing configuration info to firebase.json...
i Writing project information to .firebaserc...
✔ Firebase initialization complete!
Firebase CLI GitHub Login Successful
が表示されること
対象のリポジトリを指定していることを確認しエンターを押下
? For which GitHub repository would you like to set up a GitHub workflow? (format: user/repository) hironomiu/firebase-react-sample
y を押下
? Set up the workflow to run a build script before every deploy? Yes
yarn build
を記入
? What script should be run before every deploy? yarn build
y を押下
? Set up automatic deployment to your site's live channel when a PR is merged? Yes
main
を選択
? What is the name of the GitHub branch associated with your site's live channel? main
firebase.json
を確認
{
"hosting": {
"public": "build",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
.firebaserc
を確認
{
"projects": {
"default": "fir-react-sample"
}
}
「次へ」を押下
デプロイ(npx firebase deploy
)が成功したら「Hosting URL」をブラウザで開く
$ npx firebase deploy
=== Deploying to ‘fir-react-sample’...
i deploying hosting
i hosting[fir-react-sample]: beginning deploy...
i hosting[fir-react-sample]: found 14 files in build
✔ hosting[fir-react-sample]: file upload complete
i hosting[fir-react-sample]: finalizing version...
✔ hosting[fir-react-sample]: version finalized
i hosting[fir-react-sample]: releasing new version...
✔ hosting[fir-react-sample]: release complete
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/fir-react-sample-xxxx/overview
Hosting URL: https://fir-react-sample-xxxx.web.app
「コンソールにすすむ」を押下
ブラウザにて Hosting に設定されているドメインをクリックしアプリが動作することを確認する
ここまでに作成されたファイルを add,commit し push する
git add .
git commit -m "commited"
git push origin main
yarn build
では GitHub Actions がエラーになる場合.github/workflows/firebase-hosting-merge.yml
,.github/workflows/firebase-hosting-pull-request.yml
の- run: yarn build
を- run: yarn install && yarn build
に修正する
+ - run: yarn install && yarn build
- - run: yarn build
Secrets の設定を.github/workflows/firebase-hosting-merge.yml
,.github/workflows/firebase-hosting-pull-request.yml
のjobs:
の上に以下を追記する
env:
REACT_APP_FIREBASE_API_KEY: ${{secrets.REACT_APP_FIREBASE_API_KEY}}
REACT_APP_FIREBASE_AUTH_DOMAIN: ${{secrets.REACT_APP_FIREBASE_AUTH_DOMAIN}}
REACT_APP_FIREBASE_DATABASE_URL: ${{secrets.REACT_APP_FIREBASE_DATABASE_URL}}
REACT_APP_FIREBASE_PROJECT_ID: ${{secrets.REACT_APP_FIREBASE_PROJECT_ID}}
REACT_APP_FIREBASE_STORAGE_BUCKET: ${{secrets.REACT_APP_FIREBASE_STORAGE_BUCKET}}
REACT_APP_FIREBASE_MESSAGING_SENDER_ID: ${{secrets.REACT_APP_FIREBASE_MESSAGING_SENDER_ID}}
REACT_APP_FIREBASE_APP_ID: ${{secrets.REACT_APP_FIREBASE_APP_ID}}
GitHub リポジトリのSettings
-> Secrets
,Actions
-> New repository secret
を押下
.env.local
に設定したREACT_APP_FIREBASE_API_KEY
からREACT_APP_FIREBASE_APP_ID
を Name,Value に 1 つずつ設定しAdd secret
を押下
add,commit,push
し GitHub Actions がグリーンとなること
アプリを変更しnpx firebase deploy
やgit push
にて Hosting 先が変更されることを確認してみましょう