React Firebase 連携
構築
npx create-react-app . --template typescript
npm start
立ち上がっていればOK
必要なライブラリをインストール
npm i @material-ui/core
npm i @material-ui/icons
npm i firebase
npm i react-router-dom @types/react-router-dom
npm start
型をインストールする
yarn add -D @types/react
※reactは typescriptで書かれてない上、
型提供も元パッケージでしていないので型を別でインストールする必要がある
-Dオプション
devDependencies:開発用の依存関係
アローファンクションのテンプレートを使用できる
rafce
Firebaseとの連携
.envファイルに環境変数を設定する
REACT_APP_FIREBASE_APIKEY="" REACT_APP_FIREBASE_DOMAIN="" REACT_APP_FIREBASE_DATABASE="" REACT_APP_FIREBASE_PROJECT_ID="" REACT_APP_FIREBASE_STORAGE_BUCKET="" REACT_APP_FIREBASE_SENDER_ID="" REACT_APP_FIREBASE_APP_ID=""
firebase.tsを作成する
import firebase from "firebase/app"; import "firebase/app"; import "firebase/firestore"; import "firebase/auth"; const firebaseApp = firebase.initializeApp({ apiKey: process.env.REACT_APP_FIREBASE_APIKEY, authDomain: process.env.REACT_APP_FIREBASE_DOMAIN, databaseURL: process.env.REACT_APP_FIREBASE_DATABASE, projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID, storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET, messageingSenderId: process.env.REACT_APP_FIREBASE_SENDER_ID, appId: process.env.REACT_APP_FIREBASE_APP_ID, }); export const db = firebaseApp.firestore(); export const auth = firebase.auth();
Firebaseにてデータベースを作成する
コレクションを開始
ドキュメントIDを自動生成
フィールド入力(titleなど)
タイプを指定(stringなど)
値を入力(任意)
React側で取得する
import { List } from "@material-ui/core"; import React,{useState, useEffect} from 'react'; import './App.css'; import { db } from "./firebase"; const App: React.FC = () => { const [tasks, setTasks] = useState([{id:"", title:""}]); useEffect(()=> { const unSub = db.collection("tasks").onSnapshot((snapshot)=>{ setTasks( snapshot.docs.map((doc)=> ({id: doc.id, title: doc.data().title})) ); }); return ()=> unSub(); },[]); return ( <div className="App"> {tasks.map((task) => ( <h3>{task.title}</h3> ))} </div> ); }; export default App;
firebaseから取得したtasksの内容をReact側でstateとして保持しておきたいためuseStateを使用する
// 空の内容で初期化 const [tasks, setTasks] = useState([{id:"", title:""}]);
アプリケーションが立ち上がった時にfirebaseへアクセスして
存在するデータベースのtasksの内容を取得するためuseEffectを使用する
アプリケーションが起動した最初の1回のみデータを読みに行きたいため
第二引数は空にする
useEffect(()=> {},[]);
実際にデータベースへアクセスする内容
db.collectionで取得したいデータベース名を指定する
onSnapshotでデータベースの内容を取得する
firestoreから取得した内容を(snapshot)の引数にいれて実際の処理を行う
取得したtasksのオブジェクトの一覧をsetTasksを使ってtasksのstateに格納する
snapshotの複数あるdocumentをmapで展開する
useEffect(()=> { const unSub = db.collection("tasks").onSnapshot((snapshot)=>{ setTasks( snapshot.docs.map((doc)=> ({id: doc.id, title: doc.data().title})) ); }); return ()=> unSub(); },[]);
取得した内容をブラウザへ表示する
const App: React.FC = () => { const [tasks, setTasks] = useState([{id:"", title:""}]); useEffect(()=> { const unSub = db.collection("tasks").onSnapshot((snapshot)=>{ setTasks( snapshot.docs.map((doc)=> ({id: doc.id, title: doc.data().title})) ); }); return ()=> unSub(); },[]); return ( <div className="App"> {tasks.map((task) => ( <h3 key={task.id}>{task.title}</h3> ))} </div> ); };
タスクを作る
import { FormControl, TextField } from "@material-ui/core"; import React,{useState, useEffect} from 'react'; import './App.css'; import { db } from "./firebase"; import AddToPhotosIcon from "@material-ui/icons/AddToPhotos"; const App: React.FC = () => { const [tasks, setTasks] = useState([{id:"", title:""}]); const [input, setInput] = useState(""); useEffect(()=> { const unSub = db.collection("tasks").onSnapshot((snapshot)=>{ setTasks( snapshot.docs.map((doc)=> ({id: doc.id, title: doc.data().title})) ); }); return ()=> unSub(); },[]); const newTask = (e: React.MouseEvent<HTMLButtonElement>)=>{ db.collection("tasks").add({title: input}); setInput(""); } return ( <div className="App"> <h1>Todo App by React/Firebase</h1> <FormControl> <TextField InputLabelProps={{ shrink: true, }} label="New task ?" value={input} onChange={(e: React.ChangeEvent<HTMLInputElement>) => setInput(e.target.value)} /> </FormControl> <button disabled={!input} onClick={newTask}> <AddToPhotosIcon /> </button> {tasks.map((task) => ( <h3 key={task.id}>{task.title}</h3> ))} </div> ); }; export default App;
ユーザーがタイピングした文字列を保持するためのstateをuseStateを使用して実行する
// 初期値を空にセット const [input, setInput] = useState("");
material-uiを使用してフォームを作成する
import { FormControl, TextField } from "@material-ui/core"; <FormControl> <TextField InputLabelProps={{ shrink: true, }} label="New task ?" value={input} // ユーザーがタイピングするために呼び出される関数を定義 // input stateの内容を上書きするためsetInputを毎回呼び出す(ユーザーがタイピングしたvalueの値を取得している) // typescriptではイベントオブジェクトに型を指定する必要がある onChange={(e: React.ChangeEvent<HTMLInputElement>) => setInput(e.target.value)} /> </FormControl>
送信用のボタンを作成する
import AddToPhotosIcon from "@material-ui/icons/AddToPhotos"; // 入力フォームが空の時はボタンを押せない // ボタンが押された時に呼び出される関数(newTask)を指定する <button disabled={!input} onClick={newTask}> <AddToPhotosIcon /> </button>
ボタンを押した時の呼び出される関数を作成
const newTask = (e: React.MouseEvent<HTMLButtonElement>)=>{ // firebaseのデータベースにタスクを追加するため、db.collectionで追加したいコレクションを指定する(addでオブジェクトを渡す) db.collection("tasks").add({title: input}); // 次の新しいタスクの作成に備えてinput stateを初期化しておく setInput(""); }
※onClickなどのデータ型を知りたい場合は、onClickにカーソルを合わせると表示される