見出し画像

TypeScript & React & Firebase で何かつくってみる15 React Router

連続投稿記録も切れて少し楽になった. これからはペースを落として投稿する.

部屋ごとにURLをつくりたい

性能改善を一旦諦め, 仕様の方を詰めていくことにする.

現在の実装は, トップページに入るといきなり全員共有のゲーム画面がでてくる. まぁ実際にはありえない.  おそらく「部屋を作って招待する」ような仕組みがよいと思われる.

「まずアカウント作ってログインして友だち追加して・・・」みたいな流れをよく見るが, 使う側のハードルが高いのでちょっと避けたい.

「Firebase Authentication をいじってみたい」という意味では ログイン的な何かを実装してみるかもしれない.
が, できれば 部屋をつくると ランダムな部屋URL が生成されてそのURLを共有するだけで参加できる みたいなやつがいい.

で Firebase+React でそういった仕組みを作る方法をいろいろ調べてみる.

React Router

なんとなく存在は知っていたがよくわかっていなかった.

「SPA で内部的にパス文字列を使ってページを管理できる」くらいに思っていたが, どうも '/data' にアクセスすれば URL もちゃんと '/data' になるらしい.

「SPAで別URLってどゆこと?」
「ビルド時に複数のページを作っているのか?」

と仕組みがチンプンカンプンだったが, 答えを以下のサイトで見つけた.

実は、ちょうどいい機能がHTML5で追加されています。History APIです。
(中略)
この挙動の興味深いところは、アドレスバーのURLが変わっても、表示しているHTMLファイルを変えるわけではないというところです。

HTML5の仕様, つまりブラウザの機能だった. この機能を使えば「URLが変わったことにしてくれる」というわけだ.


仕組みがわかって安心したのでこれを使ってみる.
例によって古い記事が多いため 新しそうな解説を探す.

私は Vue.js エンジニアではないが さほど困ることもなかった.

$ yarn add react-router-dom

App.tsx

import React from 'react';
import './App.css';
import CardGame from './components/CardGame';
import { BrowserRouter, Route } from 'react-router-dom';   // 追加

function App() {
 return (
   <BrowserRouter>                                         // 追加
     <div className="App">
       <div className="App-header">
         <h2>Playing Cards</h2>
       </div>
       <Route path="/rooms/:roomId" component={CardGame}/> // 変更
     </div>
   </BrowserRouter>                                        // 追加
 );
}

export default App;

あまりちゃんと調べていないが path 文字列内で ':' 付きの要素を書くことでその場所を変数として取り出せるらしい.

取り出した変数は React Hooks API では useParams() で取得できる.

CardGame.tsx

const CardGame: React.FC = () => {
 const { roomId } = useParams();

    :

簡単なのにコード間の疎結合っぷりがすごい.

あとは firestore のパスをすべて 'rooms/${roomID}' の下に移動する.
・クライアントコード
・Cloud Functions
・ firestore.rules

これで これまでテストしていた 'http://localhost:3000' にアクセスしてもタイトルしか出なくなる. 代わりに 'http://localhost:3000/rooms/abc'  にアクセスできるようになる.

'abc' の部分が roomID だ. 今のところ roomID の値はなんでも動く.

部屋をつくる

後々 roomID は firestore にランダムなものを付けてもらうつもりだが, 一旦簡単な部屋作り機能を実装する.

クライアントコードで 全カード情報 decks を null ありの型にする.

  const [decks, setDecks] = useState<Map<string, DeckView>|null>(null);

描画処理 は null の場合, 空の場合, それ以外 の3パターンで表示を変える.

 // null のときは ロード中 
 if (!decks) {
   return <h1>Loading...</h1>;
 }
 // 空の時は 「部屋が作られていない状態」なので「部屋作成ボタン」を表示する.
 if (decks.size <= 0) {
   return (
     <div>
     <button onClick={e => createRoom()}>あたらしい部屋を作成</button>
   </div>
   );
 }
 // 通常のゲーム画面.
 return (
   <div>
    :

「あたらしい部屋を作成」ボタンを押すと, これまでの 初期化処理と同様に「箱から出したばかりのカード集合」を生成する.

画像1


今の部屋作成手順はこうだ.
1. /rooms/{適当な文字列}  にアクセスする.
2. 「あたらしい部屋を作成」ボタンを押す.
3. firestore に /rooms/{適当な文字列} ドキュメントが作られ, 初期カード情報が作成される.

これを以下のようにしたい..
1. / にアクセスする.
2. 「あたらしい部屋を作成」ボタンを押す.
3. firestore に /rooms/{ランダムなID} ドキュメントが作られ, 初期カード情報が作成される.
4. ユーザーは /rooms/{ランダムなID} に遷移する.

これは次回.

この記事が気に入ったらサポートをしてみませんか?