React Native for Web + TypeScript + Firebase + PWAを調べたのでまとめておく

動機

きっかけはこの記事。ちょっと前にこれを読んで、 「モバイルネイティブアプリに慣れた開発者が、React Native for Webを利用することで手軽にWebアプリ開発できないか?」と思ってたので調べてみました。

iOSについて言えばSwiftUI登場後、宣言的にUIを構築するということが自然となり、Webアプリの各種FW(Reactなど)との距離感が縮まっていること。前述の記事にあるように、ネイティブ開発で馴染みのあるコンポーネントをReact Nativeを経由してWebの世界に持っていけるなら、Webアプリ開発の敷居が下げられるんじゃないかと思って調べてみました。

先に結論

React Native for Webを使って簡単なTODOアプリを作ってみました。 一応Firebase Hostingに置いてPWAとしてモバイルにインストールするところまで試しました。

github.com

感想としては、

  • TypeScriptのありがたみ半端なかった。普段コンパイル言語触っててからの生JSは不安しかないのでほぼ必須に感じた。
  • UIコンポーネントのスタイル(サイズや色など)はCSS風な記述なので、普段CSSを触ってない人的にはちょい辛かった。とはいえ、for WebだけでなくReact Nativeでのネイティブアプリでもそのまま流用できそうなのは良さそう。
  • HTML/CSS/JSの知識はやはり多少は必要。あとReactなので当然そこの知識も。だけどあまりDOMを意識しないでアプリを書ける嬉しさはあった。
  • 自分が今Webアプリ作るとなったら選択肢としてはありかも。だけど、React Native for Webは個人プロジェクト?ぽく、Facebookで開発されているReact Nativeと立ち位置が異なるので今後は気になる。(追記:と書いてたら@Nkznさんに↓と教えて頂けた!感謝!!)

調べたこと

Webアプリ開発の事前知識としてはHTML/CSS/JavaScriptのほんの基本がわかる程度で、Reactも今回初めてちゃんと触ってみた感じです。ので、若干怪しいところはあるかも。。

あと、React NativeでiOS/Androidのネイティブアプリも開発する場合に、for Webのコードとはある程度共有できる期待はありますが、ここでは一旦考えていません。単純にWebのみでの動作を考えてます。

React Native for Webプロジェクトの作成方法

ドキュメントによるとExpoを利用する方法とCreate React Appを利用する方法の2つがあります。

前者はReactアプリのための様々な便利ツールがパッケージにされたフレームワークらしく、今回はシンプルに最低限で試したかったので後者を使いました。

下記のコマンドでReactプロジェクトを作成し、react-native-webをインストールします。 また、TypeScriptで書きたいので --template typescript を指定してます。

npx create-react-app todo --template typescript
cd todo

この段階で npm start で起動、 npm run build でビルドができます。

そして下記のコマンドでreact-native-webをインストールします。 (TypeScriptなので@types/..の型定義も必要になります。)

npm install react-native-web
npm install @types/react-native --save-dev

これで、プロジェクト内で import { ... } from 'react-native'; とすることでReact Nativeと互換性のあるUIコンポーネントを利用できるようになります。 Webアプリ開発の場合には react-nativeエイリアスによって react-native-web として解釈され、一方、ネイティブアプリ開発の場合は本家の react-native を参照するようにして、コード上は統一的に記述することできるようです。

React Native UIコンポーネント

基本的にはReact Nativeで用意されているコンポーネントに互換性のあるWeb版コンポーネントが用意されていますが、現状では未対応のものもいくつかありました。

利用可能なコンポーネントドキュメントGitHubに一覧があるのでそれを確認します。とりあえず最低限は揃っている印象。

React Nativeでは公式に提供されるもの以外にコミュニティ主導で開発されてるコンポーネントも多いようですが、それらがWeb側にそのまま持ってこれるかは未確認です。

Reactでのコンポーネントの作成

コンポーネントを作成する時は、クラスコンポーネントと関数コンポーネントの2択あって、関数コンポーネントが後発のようです。(https://ja.reactjs.org/docs/components-and-props.html

コンポーネントがクラスでなく関数になるので状態やライフサイクルを持たないことになるので、 それらの要素にアクセスするためにHooks APIなるものが導入されています。 (関数コンポーネントユニットテストどうするの?と思うけど今回はパス)

関数コンポーネントはまだ新しく過渡期?だけど、今後は関数コンポーネントとHooks APIを組み合わせる方向になる雰囲気のようでした。

アプリの状態管理

ベストな手法は?Reactのステート管理方法まとめ という記事を参考にしました。

ざっくり言うと、アプリが小規模であったりコンポーネント間で状態を共有する必要がなかったりする場合はReactが標準で提供するHooks APIを利用するだけで十分。より複雑な状態管理が必要であればReduxの導入を検討するのが良さそうでした。 今回作成したサンプルアプリでは前者の方法を採っています。

Reduxの場合はこのライブラリを利用することになりそう。このサイトのドキュメントもReduxの考え方の解説など、とてもわかりやすく記述されてて参考になりました。

画面遷移

画面遷移(ルーティング)を扱うライブラリはいくつかあって、どのように実装するのが定石なのかパッとわかりませんでした。

React Nativeの場合はReact Navigationあたりを利用するみたい。ですがReact Navigation on the Webのページには Support for web in React Navigation 5 is experimental と書かれててfor Webで使えるかわからなかったので未検証。react-router-domが一般的なReactアプリで利用されてるようなので今回はそちらを使ってみました。

Firebase

React Native for WebのアプリでFirebaseを利用する方法を調べました。 結局は普通のWebアプリなので、一般的なWebアプリと同じセットアップで導入できます。 つまりこれだけ $ npm install firebase

FirebaseのAPIキーなどの設定は様々なんだと思いますが、create-react-appしたプロジェクトでは.envに記述したキーと値のペアを自動的に環境変数に読み込んでくれるので、今回はそれを利用しています。めっちゃ楽。 (ちゃんと調べてないけどdotenvライブラリが動いてるみたい)

今回のサンプルアプリではFirestoreを利用してますが、それも特別なことはなさそう。 Firebase Hostingにデプロイする際にはビルドが必要になので以下の作業が必要でした。

  • $ npm start build でプロジェクトをビルドする。デフォルトでは build ディレクトリに成果物が生成される
  • firebase.jsonでこのディレクトリを公開設定

PWA

実は今回はPWAについてはあんまり手をだせてないです。

ですが、PWAにする最低条件としてはWeb App ManifestとService Workerを用意する必要があるようですが、 create-react-app で作成したプロジェクトには最初からそれらが用意されているので、取り敢えずシュッと試してみることができます。 (他にHTTPSでの配信が必須ですがFirebase HostingでそれもOK)

この1行unregister() -> register() に変えただけで、iOSAndroidなどのブラウザで「ホーム画面に追加」(A2HS)できます。

PWAの詳細は https://developer.mozilla.org/ja/docs/Web/Progressive_web_apps にあって、特にService Workerは一旦インストールされると後から更新が難しかったりするので、実際に利用する際には気をつけた方がよさそうです。

その他の参考記事