7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【超簡単】React×Firebaseで投稿機能作ってみた〜

Posted at

やること

ReactとFirebaseを使って超簡単な投稿アプリを作ります!
ダウンロード.gif

Reactアプリの作成

terminal
npx create-react-app post-app
cd post-app
npm install firebase
npm install -g firebase-tools

srcフォルダ内にAllPosts.jsNewPost.jsファイルを作成(コンポーネント)

src/AllPosts.js
import React from 'react'

const AllPosts = () => {
    return (
        <>
            <p>投稿一覧</p>
        </>
    )
}

export default AllPosts
src/NewPost.js
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
src/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;
terminal
npm start

image.png
こんな感じになってればおけです!

firebase側の設定

このリンクにアクセスする
ログインしといてください!!!
image.png
「プロジェクトを追加」をクリック
image.png

とりあえず「続行」押しまくってプロジェクト作成すればオッケーです!!
image.png
こんな感じのとこに行ってれおけです!!
Screenshot 2022-12-22 10.25.19.png
写真のとこ</>クリック
image.png
Screenshot 2022-12-22 10.28.04.png
写真のとこにAPIkey等いろいろ書いてあるのでコピーしといてください

コピーしたものはsrcフォルダにfirebase.jsというファイルを作成して貼っつけときましょー

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);

次に、
Screenshot 2022-12-22 10.34.49.png
「構築」の中の「Firebase Database」をクリック
image.png
「データベースの作成」をクリック
image.png
テストモードで開始
そのまま有効にするで大丈夫です
Screenshot 2022-12-22 10.38.39.png
次にルールを少し書き換えます
image.png
写真の選択している箇所を消します
image.png
「公開」をクリックします
ここまでで、firestore側の設定は終了です!

React側書いていくぞおお!!

ここからはreactで先ほどコピーしたAPIkeyを使って投稿機能を作っていきます!

src/firebase.js
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からいきましょー

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

解説

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についてわからない人はググったらいっぱい出てくるので解説は省略します:bow_tone2:
今回はpostsコレクションに、useStateで指定しているtitleとcontentを追加しています
一応省略できます(今回は辞書型であることを意識するために長い方で書いてます)

const onSubmit = async (e) => {
    e.preventDefault();
    try {
        await addDoc(collection(db, "posts"), {
            title, //省略
            content //省略
        });
    } catch (error) {
        console.log(error);
    }
};

ここで一旦サイトを立ち上げてみましょう

terminal
npm start

特に見た目は変わっていないと思いますが、入力フォームに適当に入力して投稿してみましょう
まだ表示部分は作っていないので、表示はされませんが、firebaseのサイトに移動するとデータが書き込まれていることがわかります
image.png
image.png
投稿の一覧表示していきます

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

解説

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()で取得した数字↓↓
image.png
こんな感じ
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
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
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
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
7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?