FirebaseでReact Routerを使うには?

記事
IT・テクノロジー

FirebaseでReact Routerを使うには?

FirebaseとReactという組み合わせは、Webアプリ、Webサービスを作る場合よく利用される組み合わせです。
Reactと合わせて利用される機能にReact Router (react-router-dom)があります。これは、React内でページを切り替えるための機能の拡張ですが、実際のWebサイトのページを切り替える感覚で利用できるので便利な機能です。
例えば
sv-sw.com/firebase

のようにドメインに続いて「firebase」のようにパス(Path)を指定すると、そのページがあるかの様に表示を切り替えてくれる機能です。

Reactのアプリのページは基本的に「1ページ」

よく、1ページアプリとも言われますが、実際にReactが使っているページは、通常は「index.html」だけです。Reactのプログラム内で、「firebase」というページ(のようなもの)を作った場合でも、実際のページは存在しません。
実は、この辺りが、実際にWebサーバーにReactアプリを公開しようとする場合(デプロイする場合)にちょっと混乱する部分です。

Webホスト(サーバー)が管理している「ルーティング」

元々は、Webサイトの全てのHTMLファイルはWebホスティングしているWebサーバーにおいてあって、ブラウザから要求されたファイルをWebホストが提供していました。最近のWebサイトは実際のファイルがない場合も多く、仮想ファイルをサーバーが判断してWebブラウザに提供しています。
サーバー側でのレンダリングの場合はサーバー側で指定されたパスの処理をしています。こうした、Webブラウザでドメインの後に指定されたパスに応じて必要な情報をWebブラウザに提供する事を「ルーティング」と呼んでいます。
通常は、このルーティングの処理はWebホスティングをしているサーバー側で行っています。

ReactのアプリはWebブラウザで動いている!

ところが、ReactのアプリはWebブラウザ上で動いています。従って、Webブラウザでドメインの後にパスをタイプした場合、通常はサーバにその情報が送られてWebホスティングをしているサーバーが処理しようとします。Reactアプリで、そのページを表示しようとしても、サーバーはその場所を見つけられないのでエラーになります。
この処理をきちんと行うにはWebホスティングしているサーバーに設定をする必要があります。

開発用のPCでテストしている時にはきちんと動作する!?

多くの方が混乱する理由に、開発用のPCでテスト用のサーバーを使ってテストしている場合はちゃんと動く場合が殆どです。 Reactの場合は
$ npm start

で開発用のサーバーを起動すると、通常は「localhost:3000」で、Reactのアプリを起動できます。この時は特別に設定をしていなくても、React Routerの機能はきちんと動作します。
これは、このサーバー側では「ルーティング処理」をしていないからです。
Firebaseでデプロイすると挙動が違う!
ところが、公開用のイメージを作ってFirebaseのホスティング機能を使ってインターネットで公開すると、きちんと設定していない場合には動きません。Reactの仮想ページを表示しようとすると、Firebaseのサーバーはページが存在しないとエラーにしてしまいます。
Reactで作ったページは、Webホスティングしているサーバー側は知らないからです。

Firebaseの設定方法

Firebaseの場合は、Firebaseのプロジェクトフォルダー(firebase init)を実行したフォルダに作られる「firebase.json」の設定をするとこの問題を回避できます。
以下に設定の例を示します!
{
  "hosting": {
    "public": "public",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ],
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
  },
  "storage": {
    "rules": "storage.rules"
  }
}

追加したのは、「hosting」の中の「rewrites」という設定です。 この設定は、Webブラウザからどんなパスが要求されても、全部「index.html」に任せれば良いよ!という設定です。 要は、Webホスティングのサーバーは全部index.htmlに任せて何もしなくてよいという設定です。
"rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ],

この設定をした後に、「firebase deploy」を実行すれば、React Routerが全てを管理できるようになって、インターネットに公開してもきちんと動作します。

問題は。。。

ReactのアプリだけをWebホスティングする場合は上の挙げた方法で問題は解決です!実は、問題がある場合もあります。
それは、Firebaseでバックエンドの実装をしている場合です。
この場合は、バックエンド側の処理で、必要なパスを処理しなくてはいけないので、全部をReactに任せる事が出来なくなるからです。 そうなると、何がバックエンドで処理して、何がReactで処理するのかを全て列挙しないといけなくなります。
バックエンドの処理(「app」というファンクションで処理)する場合の記述は、
"rewrites":[
    {
        "source":"/query",
        "function": "app",
    },
    {
        "source":"/checkout",
        "function":"app",
    },
    {
        "source": "**",
        "destination":"/index.html",
    }
]
のようにもっと複雑な設定になります。 これは、「/query」と「/checkout」はバックエンドの「app」で処理して、それ以外はReactに任せるという書き方です。
概ね、きちんと設定すれば実装上は問題はない事になります。

お勧めは、Reactのアプリとバックエンドでホスティングを分ける!

上のように設定すれば動くはずですが、上手く行かない場合も結構あります。特に、「/」や「index.html」が絡むと上手く設定できない場合が多いです。やり方はあると思いますが、トラブルを引き起こす可能性を考えると、Reactのアプリと、バックエンドのサービスのホスティングを別々にすれば、問題は解決します。この方法の方がトラブルは少なくなります。

Firebaseは複数のホスティングもサポートしている

Firebaseは一つのプロジェクトの中で、別の機能を別のWebホスティングで扱うことができます。Reactのアプリとバックエンドを一つのホスティングで実装する事も可能ですが、分けてしまった方がシンプルになる場合が多いので、まずは分ける事を検討される事をお勧めします。
分けた場合の設定は バックエンド側は
rewrites:[
    {
        "source":"**",
        "function":"app"
    }
]

「app」というバックエンドのファンクションで処理する場合は全て、「app」で処理する設定です。
Reactアプリの場合は、別のホスティングを追加して、

rewites:[

    {
        "source":"**",
        "destination:"/index.html"
    }
]

のようにして、全てをindex.htmlに任せる設定です。
この方法の方が確実に動作して、間違いも少ないので初心者には絶対お勧めです。

まとめ

ReactでReact Routerを利用している場合は、インターネットに公開する際(デプロイする際)にサーバー側の設定が必要になります。 FirebaseでWebホスティングする場合、firebase.jsonファイルに、「rewites」の設定を追加するだけで簡単にできます。
ただし、バックエンドのサービスと、Reactのアプリを同じホスティングで公開する場合は注意が必要です。お勧めはバックエンドとReactのアプリは別のホスティングで公開する方が無難です。設定もシンプルで、確実に動作します。
バックエンドのサービスを別のホスティングでデプロイする場合は、CORS(Cross-Origin Resource Sharing)の設定が必要になります。最新のブラウザーは異なるドメインからのリクエストをCORSの設定がされていない場合ブロックするようになっているためです。 CORSについては別に記事を書く予定ですのでそちらを参考にされてください。
サービス数40万件のスキルマーケット、あなたにぴったりのサービスを探す