38
41

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 5 years have passed since last update.

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

Last updated at Posted at 2018-10-18

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で動かすなどを参考にしました。

38
41
1

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
38
41

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?