Help us understand the problem. What is going on with this article?

TypeScript + React + styled-components + Firestore + Firebase HostingでWebアプリを作り始める

More than 1 year has passed since last update.

TypeScript, React, styled-components, Cloud Firestore, Firebase Hostingを使ってWebサイトを公開する方法。1

FirestoreもReactも使ったことない人(筆者もそう)」が
Firestoreに接続するReactアプリを作り、Firebase Hostingにデプロイするまで」を簡潔にまとめました。2

  • VSCodeを使っています
  • 色々な考慮を省略しています

1. Reactプロジェクトの作成

まずは必要なものインストール

firebase-tools

公式のコマンドラインツール

$ npm install -g firebase-tools

create-react-app

Facebook公式。Reactアプリの雛形を生成できる。

$ npm install -g create-react-app

雛形の生成

任意の場所で以下を実行するだけ。
オプション「--scripts-version=react-scripts-ts」により、TypeScriptを使う構成で生成してくれる。

$ create-react-app hoge-react-app --scripts-version=react-scripts-ts

VSCodeの拡張機能や設定

【Prettierについての補足】
上記のプラグインを入れた後、設定に以下を追加するだけで済む。

"[typescriptreact]": {
    "editor.formatOnSave": true
},

フォーマットのルールをカスタマイズしたい場合は、.prettierrcを作成して定義すればOK。

.prettierrc.json
{
  "singleQuote": true
}

2. Firebase Hostingへのデプロイ

Firebaseプロジェクトの生成

無料で開始」ボタンからプロジェクトを作成する。
Databaseの項目からFirestoreも開始しておく。

ターミナルからFirebaseにログインする

$ firebase login

Reactアプリをfirebase Hostingと接続する 

hoge-react-appディレクトリで以下を実行する

$ firebase init
// 「◯ Hosting: Configure and deploy Firebase Hosting sites」をスペースキーで選んでEnter
// さっき作成したプロジェクトを選んでEnter

firebase.jsonの編集

hostingのpublicを"build"にしておく。

firebase.json
{
  "hosting": {
    "public": "build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }
}

動作確認

$ npm start

deployしてみる

$ npm run build && firebase deploy

ここまでは特に面倒なこともなくスムーズにいける(はず)

3. Firestoreとの接続

こっからが本記事の本番
最終的に↓のようになる。
スクリーンショット 2018-10-22 13.39.50.png

Firestoreのルールを設定する

hoge-react-appディレクトリのルートに、firestore.rulesファイルを新規作成する。
とりあえず、書き込みだけは認証が必要な設定にしてみる。

firestore.rules
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read;
      allow write: if request.auth != null;
    }
  }
}

Firebase関連のコードを追加

srcディレクトリ配下に、firebaseディレクトリを作成し、以下のファイルを新規作成します。
(ディレクトリ名はわかりやすくfirebaseにしただけで、別になんでもいい)
ちょっとこの実装最高に雑ですが...おゆるしを...

firebase/index.tsx
import * as firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
import { FIREBASE_CONFIG } from "./config";

export default class Env {
  private static singleInstance: Env;

  public readonly firebase: firebase.app.App;
  public readonly firestore: firebase.firestore.Firestore;
  public readonly providerGoogle = new firebase.auth.GoogleAuthProvider();

  private constructor() {
    this.firebase = firebase.initializeApp(FIREBASE_CONFIG);
    this.firestore = this.firebase.firestore();
    const settings = { timestampsInSnapshots: true };
    this.firestore.settings(settings);
  }

  public static get instance(): Env {
    if (!this.singleInstance) {
      this.singleInstance = new Env();
    }
    return this.singleInstance;
  }
}
firebase/config.tsx
// 以下の情報は、Firebase-consoleから、「+アプリを追加」 > 「 </> 」を選ぶと確認できます。
export const FIREBASE_CONFIG = {
  apiKey: 'XXX',
  authDomain: 'XXX',
  databaseURL: 'XXX',
  projectId: 'XXX',
  storageBucket: 'XXX',
  messagingSenderId: 'XXX'
};

App.tsxの編集

srcディレクトリ配下のApp.tsxを編集し、Firestoreと接続しましょう。3
ボタン2つを配置するだけの単純なものです。

App.tsx
import * as React from "react";
import * as Boostrap from "react-bootstrap";
import "./App.css";
import Env from "./firebase/index";

export default class App extends React.Component {
  public render(): JSX.Element {
    return (
      <div className="App">
        <Boostrap.Button onClick={this.handleLogin}>Login</Boostrap.Button>
        <Boostrap.Button onClick={this.insertDummyUser}>
          firestore test
        </Boostrap.Button>
      </div>
    );
  }

  private handleLogin = () => {
    Env.instance.firebase
      .auth()
      .signInWithRedirect(Env.instance.providerGoogle);
    Env.instance.firebase
      .auth()
      .getRedirectResult()
      .then(result => {
        console.log(result);
      })
      .catch(error => {
        console.log(error);
      });
  };

  private insertDummyUser = () => {
    Env.instance.firestore
      .collection("users")
      .doc("dummy_user_1")
      .set({
        country: "Japan",
        name: "dummyUser1"
      })
      .then(() => {
        console.log("Success: Document has written");
      })
      .catch(error => {
        console.error("Error writing document: ", error);
      });
  };
}

完成

こんな感じになっているはず
スクリーンショット 2018-10-22 13.39.50.png

動作確認

$ npm start

こんな感じの画面が出ます。
スクリーンショット 2018-10-18 16.38.43.png

Loginしていなければ、Firestoreへのコレクション追加が失敗するのを確認しましょう。(デベロッパーツールにエラーが出るはず)
また、Fireabse-consoleから、コレクションの追加を確認しましょう。

deploy!

$ npm run build && firebase deploy

4. CSS in JSをやっていく

 最後に、styled-componentsを導入し、CSSを少し書いてみます。4

まず必要なものインストール

$ npm install --save styled-components
$ npm install @types/styled-components

App.tsxにCSSを追加

styled-componentsのサンプルCSSを参考に、ボタンの色を変えてみます。

App.tsx
import * as React from 'react';
import styled, { css } from 'styled-components';
import './App.css';
import Env from './firebase/index';

export default class App extends React.Component {
  public render() {
    return (
      <div className="App">
        <Button onClick={this.handleLogin}>Login</Button>
        <Button primary={true} onClick={this.insertDummyUser}>
          firestore test
        </Button>
      </div>
    );
  }

  private handleLogin = () => {
    Env.instance.firebase
      .auth()
      .signInWithRedirect(Env.instance.providerGoogle);
    Env.instance.firebase
      .auth()
      .getRedirectResult()
      .then(result => {
        console.log(result);
      })
      .catch(error => {
        console.log(error);
      });
  };

  private insertDummyUser = () => {
    Env.instance.firestore
      .collection('users')
      .doc('dummy_user_1')
      .set({
        country: 'Japan',
        name: 'dummyUser1'
      })
      .then(() => {
        console.log('Success: Document has written');
      })
      .catch(error => {
        console.error('Error writing document: ', error);
      });
  };
}

// styled-components
interface InterfaceMyProps {
  primary?: boolean;
  onClick: () => void;
}

const StyledButton = styled.button`
  background: transparent;
  border-radius: 3px;
  border: 2px solid palevioletred;
  color: palevioletred;
  margin: 0 1em;
  padding: 0.25em 1em;

  ${(props: InterfaceMyProps) =>
    props.primary &&
    css`
      background: palevioletred;
      color: white;
    `};
`;

const Button: React.SFC<InterfaceMyProps> = ({ ...props }) => (
  <StyledButton {...props} />
);

ちょっとここらへん強引な感がある...悩

完成

スクリーンショット 2018-10-20 17.08.23.png

あとはdeployするなり。


  1. 最初はGAE/GoとFirestoreで書いていましたが、とある重要なことにデプロイして初めて気づきました。 

  2. TypeScript + Reactについては、公式ドキュメントこの記事が分かりやすい。 

  3. このコードはPrettierの自動フォーマットに従ったままです。 

  4. 公式ドキュメントstyled-componentsを使ったCSS設計React + Storybook + styled-componentsをTypeScriptで動かすなどを参考にしました。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away