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. /rooms/{適当な文字列} にアクセスする.
2. 「あたらしい部屋を作成」ボタンを押す.
3. firestore に /rooms/{適当な文字列} ドキュメントが作られ, 初期カード情報が作成される.
これを以下のようにしたい..
1. / にアクセスする.
2. 「あたらしい部屋を作成」ボタンを押す.
3. firestore に /rooms/{ランダムなID} ドキュメントが作られ, 初期カード情報が作成される.
4. ユーザーは /rooms/{ランダムなID} に遷移する.
これは次回.
この記事が気に入ったらサポートをしてみませんか?