やること
ReactとFirebaseを使って超簡単な投稿アプリを作ります!
Reactアプリの作成
npx create-react-app post-app
cd post-app
npm install firebase
npm install -g firebase-tools
srcフォルダ内にAllPosts.js
とNewPost.js
ファイルを作成(コンポーネント)
import React from 'react'
const AllPosts = () => {
return (
<>
<p>投稿一覧</p>
</>
)
}
export default AllPosts
import React from 'react'
const NewPost = () => {
return (
<>
<p>新規投稿</p>
<form>
<input type="text" placeholder='タイトル' />
<input type="text" placeholder='内容' />
<button type='submit'>投稿</button>
</form>
</>
)
}
export default NewPost
import './App.css';
import NewPost from './NewPost';
import AllPosts from './AllPosts';
function App() {
return (
<div className="App">
<NewPost />
<AllPosts />
</div>
);
}
export default App;
npm start
firebase側の設定
このリンクにアクセスする
ログインしといてください!!!
「プロジェクトを追加」をクリック
とりあえず「続行」押しまくってプロジェクト作成すればオッケーです!!
こんな感じのとこに行ってれおけです!!
写真のとこ</>
クリック
写真のとこにAPIkey等いろいろ書いてあるのでコピーしといてください
コピーしたものはsrcフォルダにfirebase.js
というファイルを作成して貼っつけときましょー
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "**********",
authDomain: "**********",
projectId: "**********",
storageBucket: "**********",
messagingSenderId: "**********",
appId: "**********",
measurementId: "**********"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
次に、
「構築」の中の「Firebase Database」をクリック
「データベースの作成」をクリック
テストモードで開始
そのまま有効にするで大丈夫です
次にルールを少し書き換えます
写真の選択している箇所を消します
「公開」をクリックします
ここまでで、firestore側の設定は終了です!
React側書いていくぞおお!!
ここからはreactで先ほどコピーしたAPIkeyを使って投稿機能を作っていきます!
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: "**********",
authDomain: "**********",
projectId: "**********",
storageBucket: "**********",
messagingSenderId: "**********",
appId: "**********",
measurementId: "**********"
};
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
export default db;
まずは、NewPostからいきましょー
import { useState } from "react";
import { collection, addDoc } from "firebase/firestore";
import db from "./firebase";
const NewPost = () => {
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const onSubmit = async (e) => {
e.preventDefault();
try {
await addDoc(collection(db, "posts"), {
title:title,
content:content,
created_at:new Date().getTime()
});
setTitle('')
setContent('')
} catch (error) {
console.log(error);
}
};
return (
<>
<p>新規投稿</p>
<form onSubmit={onSubmit}>
<input
type="text"
value={title}
placeholder='タイトル'
onChange={(e) => setTitle(e.target.value)}
/>
<input
type="text"
value={content}
placeholder='内容'
onChange={(e) => setContent(e.target.value)}
/>
<button type='submit'>投稿</button>
</form>
</>
)
}
export default NewPost
解説
const onSubmit = async (e) => {
e.preventDefault();
try {
await addDoc(collection(db, "posts"), {
title:title,
content:content
});
} catch (error) {
console.log(error);
}
};
このonSubmit関数は投稿ボタン押された時にfiresoreへデータを書き込む処理をしています
preventDefaultは、イベントに対するデフォルトの動作を止めるメソッドです。
async-awaitは非同期処理をしています
collection(コレクション)はSQLのテーブルのようなもので、ドキュメントがレコードのようなものです
addDocは引数に指定しているコレクションにドキュメントを追加する関数です
dbはsrc/firebase.jsからインポートしています
(useStateについてわからない人はググったらいっぱい出てくるので解説は省略します)
今回はpostsコレクションに、useStateで指定しているtitleとcontentを追加しています
一応省略できます(今回は辞書型であることを意識するために長い方で書いてます)
const onSubmit = async (e) => {
e.preventDefault();
try {
await addDoc(collection(db, "posts"), {
title, //省略
content //省略
});
} catch (error) {
console.log(error);
}
};
ここで一旦サイトを立ち上げてみましょう
npm start
特に見た目は変わっていないと思いますが、入力フォームに適当に入力して投稿してみましょう
まだ表示部分は作っていないので、表示はされませんが、firebaseのサイトに移動するとデータが書き込まれていることがわかります
投稿の一覧表示していきます
import { collection, onSnapshot } from 'firebase/firestore'
import React, { useEffect, useState } from 'react'
import db from './firebase'
const getStrTime = (time) => {
let t = new Date(time);
return (`${t.getFullYear()}/${t.getMonth()+1}/${t.getDate()} ${t.getHours()}:${t.getMinutes()}:${t.getSeconds()}`);
}
const AllPosts = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
onSnapshot(collection(db, 'posts'), (posts) => {
setPosts(
posts.docs.map((post) => post.data()).sort((a,b) => b.created_at - a.created_at)
)
})
}, []);
return (
<>
<p>投稿一覧</p>
{posts.map((post) => (
<div className='post'>
<div className='title'>タイトル:{post.title}</div>
<div className='content'>内容:{post.content}</div>
<div className='created_at'>投稿日:{getStrTime(post.created_at)}</div>
</div>
))}
</>
);
}
export default AllPosts
解説
const getStrTime = (time) => {
let t = new Date(time);
return (`${t.getFullYear()}/${t.getMonth()+1}/${t.getDate()} ${t.getHours()}:${t.getMinutes()}:${t.getSeconds()}`);
}
この関数はnew Date().getTime()
で取得した数字を表示用の文字列に変換しています
new Date().getTime()
で取得した数字↓↓
こんな感じ
useEffectはロードしてるだけですが、今回はonSnapshotっていうfirestoreの関数を使ってリアルタイムで投稿を取得しています。これがなかったらリロードしないと最新の投稿を取得できません。
onSnapshot(collection(db, 'posts'), (posts) => {
setPosts(
posts.docs.map((post) => post.data()).sort((a,b) => b.created_at - a.created_at)
)
})
map
で投稿のデータを取ってきてsort
でcreated_atの最新の投稿順で表示しています
あとは表示してるだけです。
全体まとめ
App.js
import './App.css';
import NewPost from './NewPost';
import AllPosts from './AllPosts';
function App() {
return (
<div className="App">
<NewPost />
<AllPosts />
</div>
);
}
export default App;
NewPost.js
import { useState } from "react";
import { collection, addDoc } from "firebase/firestore";
import db from "./firebase";
const NewPost = () => {
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const onSubmit = async (e) => {
e.preventDefault();
try {
await addDoc(collection(db, "posts"), {
title:title,
content:content,
created_at:new Date().getTime()
});
setTitle('')
setContent('')
} catch (error) {
console.log(error);
}
};
return (
<>
<p>新規投稿</p>
<form onSubmit={onSubmit}>
<input
type="text"
value={title}
placeholder='タイトル'
onChange={(e) => setTitle(e.target.value)}
/>
<input
type="text"
value={content}
placeholder='内容'
onChange={(e) => setContent(e.target.value)}
/>
<button type='submit'>投稿</button>
</form>
</>
)
}
export default NewPost
AllPosts.js
import { collection, onSnapshot } from 'firebase/firestore'
import React, { useEffect, useState } from 'react'
import db from './firebase'
const getStrTime = (time) => {
let t = new Date(time);
return (`${t.getFullYear()}/${t.getMonth()+1}/${t.getDate()} ${t.getHours()}:${t.getMinutes()}:${t.getSeconds()}`);
}
const AllPosts = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
onSnapshot(collection(db, 'posts'), (posts) => {
setPosts(
posts.docs.map((post) => post.data()).sort((a,b) => b.created_at - a.created_at)
)
})
}, []);
return (
<>
<p>投稿一覧</p>
{posts.map((post) => (
<div className='post'>
<div className='title'>タイトル:{post.title}</div>
<div className='content'>内容:{post.content}</div>
<div className='created_at'>投稿日:{getStrTime(post.created_at)}</div>
</div>
))}
</>
);
}
export default AllPosts