Relay Modernに移行した
今作ってるアプリ、Lylica - 街のおすすめが分かるSNSをRelay ClassicからRelay Modernに移行した。
動機
今後機能を追加していくにあたり、今のうちにえいやとやっておきたかったからだ(とある機能の追加に腰が重かったから先にこっちに着手したというのは内緒)。
以前 Relay Modernへの移行検討をした - taiki-t's diary という記事を書いてその時にはいわゆるlocal stateがサポートされるまでModernに移行するつもりはないと言っていたけど、結局まだサポートされていない*1まま移行した。まあえいやって思っちゃったから。
パフォーマンス
体感的にまあアプリのパフォーマンス的にはそこまで変わりはない。計測はしてない。OptimisticResponse
はClassicの頃から最強。サーバから返ってくるであろうデータをあらかじめ定義することで、サーバからデータが返ってきたものとしてビューを更新してくれるので爆速。サーバー側のエラーなどでデータ更新ができなかった場合は自動でロールバックしてくれる。更新できたら見た目にはそのまま。さいつよさしかない。バックエンドherokuで動かしてレイテンシ800msとかあっても感じさせない、というか原理的に見ためには存在しない。やばい。
気分
気は楽になった。Classicのスタイルでコンポーネントやmutationを追加するときに「うっ」って思ってたのが消えた。 そして、来たるSubscriptionsに対応できるぞーという楽しみができた。よかった。最高。
かかった手間
これ:
計36時間 46 commits
— taiki-🇹 (@taiki__t) August 17, 2017
Relayを使った30個のコンポーネントをModernに置き換えるために使った時間
まだmutationsがある
自動生成も含めて変更されたファイルを含めると 103 files, +10,022, -1,317 linesという感じ
上記はほぼmutations以外のRelayを使ったコンポーネントを置き換えた時間。スパイク的に1箇所ぐらいmutationも書き換えたと思う。 で、上記以外のmutationsの書き換えにかかった時間は、9時間53分*2。1日かけて15個ほど書き換えた*3。
手順
親切なことに移行の手順書みたいなのが用意されているのでそれをベースにやった。
Conversion Playbook - Relay Docs
上記の手順通りにうまくいったのは Step 0: Install Relay v1.0
までだった
Step 1: Incrementally convert to Relay Compat
で、用意されていた移行スクリプト(まじ親切)を実行し、
シンプルなコンポーネントは自動でcompatモードに移行できた。
しかしその後、「手動でやらなきゃいけないよ」と上述のスクリプトが親切にコメントインしてくれた箇所は、compat
モードで動かないことが試行錯誤ののち判明した。
ので、該当箇所は1箇所ずつ手動でいきなり Modernにえいやしていくことにした。
結果として手順は
- Relay Modernのインストール
- スクリプトで一部コンポーネントをcompatモードに移行
- ファイル名をCamelCaseに変更
- 自動でできなかったところをcompatを経由せず一つ一ついきなりModernに移行
- 実験的に途中1箇所mutationを置き換え
- データ取得については全てModernに移行(Relayの読み込み元を
react-relay/compat
からreact-relay
に移行) - mutationsを一つ一つ手で書き換え
- 途中cacheを実装。Relay Modernではデータのcacheは自動でやらなくなったため。cacheの実装については別途記事にしようかと思う。
- 完全にRelay Modernに移行 (
react-relay/compat
,react-relay/classic
からの読み込みを全てreact-relay
に。)
ハマったとこ
コンポーネント、ファイル名
手順にもちょろっと書いたけど これ:
Relay Modern compatモードで動かしてるんだけど、fragment名についてcomplierではファイル名に依存してて、runtime(?)ではコンポーネント名に依存してる… snake caseとcamel caseにしてるからファイル名変えないと…
— taiki-🇹 (@taiki__t) August 9, 2017
compat(compatible とは言っていない)…! https://t.co/Pd6vE7eiJL
— Yosuke Kurami (@Quramy) August 10, 2017
ので、ファイル名とimportしてるところをガッと書き換えた。そこまでやって、一部機能がcomapモードに対応してないことが試行錯誤の結果判明したので結局いきなりModernにいくことに。
Rootのクエリ名
上記についてはまあ最初からファイル名もCamelCaseで書いてある人には関係のない話だったろう。Facebook内部ではそういう規約でやってるんだと思う。他、ルートとなるクエリの名前にもFacebook内部の仕様が漏れ出ててる雰囲気があって、Query
っていう名前じゃないとcompat
モードを通せなかった。RootQuery
ってしてたからそこもはまった。この制約はcompatモードのみで、Modernに完全に移行してしまえば関係ない。関係あったらアプリの後方互換性的にしんどかったなあ。この問題は今後のアップデートで解消される予定。
キャッシュ
キャッシュをするなら手で有効にする必要がある。キャッシュ機構自体はあるので、それをちょろちょろっと組み合わせればできる。でも知らなかったしドキュメントもなさげ。
追記 2017-09-05: キャッシュの実装についてサンプルを書いた: Implement a simple caching for Relay Modern. - taiki-t's diary
Connections
例えばモデルとしてポストがあって、ポストがたくさんコメントを持つというとき、コメントを取り扱うのがconnections。has manyな関係を取り扱う感じ。書き方がだいぶ変わってて、あとcompatモードじゃ動かなくて、ドキュメントも薄かった。まあやった。自動で変換してくれないところの一つ。てか自動で変換してくれないところはそれぞれやはりつらみがある、 Relay 1.2.0が都合よく移行中に出てきたので、それで助かった部分もある。Connectionsの再取得の書き方など、1.2.0より前は大変そうだった。
mutation成功、失敗時の処理
ClassicではonFailure
, onSuccess
的な二つのコールバックでサーバーからのエラー、成功時処理をそれぞれ書いてた。Modernになってからは見た目にはonError
とonCompleted
というコールバックになってて、そのままClassicの感じかなと思ってたら挙動が異なっていた。サーバーからのレスポンスは失敗でも成功でもonCompleted
で処理するようになっててonCompleted
でサーバーから返されたエラーのハンドリングができるとはwebのドキュメントにはなかった(レポジトリのやつは更新されてた)。onError
はRelay
がエラーに直面した時に呼ばれる。
さいごに
全体的な感想としては、やってよかったなと。書くようになったところと書かなくてよくなったところのバランスが、Classicと比べてよくなってると思う。特にmutations周りはよくできてるなーと言いたくなった。ただ、書くようになったところのドキュメントがまだ薄いので、ね。ドキュメントもっと充実するといい。
まだ色々あったようなきがするけど疲れたのでここら辺で。気が向いたら or リクエストがあれば追記しようと思う。ところでこの記事誰かの役に立つのだろうか?笑
リンク
Relay - A JavaScript framework for building data-driven React applications
GraphQL error handling in Relay Modern · Issue #1913 · facebook/relay · GitHub
[Modern] Paginating a connection in the root query · Issue #1705 · facebook/relay · GitHub
Lylica - 街のおすすめが分かるSNSを App Store で
*1:機能はあるかもしれないけどドキュメントがなく誰も使い方がわからない [Modern] Client schema docs and examples · Issue #1656 · facebook/relay · GitHub
*2:
計測には RescueTime 使ってる。いい。
*3:今は自分しか開発陣はいないので、やりたい時にやるスタイル。逆に4時間とかしか稼働しない時もある