
2025.02.12
職員一人あたり52時間の残業削減に成功 kintone導入がもたらした富士吉田市の自治体DX“変革”ハウツー
Suspense(全1記事)
リンクをコピー
記事をブックマーク
koba04氏:よろしくお願いします。「Suspense」というタイトルで話していきたいと思います。Suspenseを聞いたことがある方はどれぐらいいますか?
(会場挙手)
数人ぐらいですね。ありがとうございます。
最初に自己紹介をさせていただきます。「koba04」というアカウントでやっています。
今はサイボウズで働いていて、フロントエンド周りいろいろやっています。OSS関連では、React系にコントリビュートしていることが多いです。
最初にSuspenseとはなにかという話です。2月の後半のJSConf IcelandでDan Abramovによって発表されたのが最初です。
もう1つ、(React)17の目玉の機能として「Time Slicing」というものがあります。そっちは低スペックなモバイルでもUIをブロッキングせずに更新できるようになる機能で、Suspenseは非同期の更新処理をいい感じに扱うための機能というイメージです。
非同期の更新処理というのはなにかというと、例えばHTTP Requestとか、最近だとDynamic importなど、基本的にPromiseをprimitiveとして扱うようなもの。なので、Promiseを返すようなものだったらだいたいなんでも扱えます。
非同期の更新処理って面倒くさいなと思っているかと思います。例えばReduxとかで実装すると、Start、Success、Failureとかのアクションを3つぐらい発行してloadingのstateを管理してみたいな、「あぁ、つらい……」みたいな感じになると思います。
(会場笑)
あと、そこをちゃんとしようとすると面倒くさくて。よくある場面としては、いろんなコンポーネント単位でLoadingのスピナーがバーって5〜6個出ちゃったりとか、APIが100msぐらいで返ってきてるのに「loading...」というのが一瞬だけちらっと出たりして、すごいジャンクな感じ。
このあたりをちゃんとやろうとすると面倒くさいのはけっこう同意できる点じゃないかと思います。そのあたりを解決するための機能がSuspenseです。
では、これはPromise supportかと思うと思いますが、ComponentがPromiseを返すのをsupportするというわけではありません。
ただPromiseを返すComponentだけsupportした場合でもこの問題は解決できません。例えば、子や孫のComponentが非同期の依存関係を持っているときは、全部親のComponentや、Loading出したいところまでリフトアップしてこないとダメで、それを手動でやるとするとだいぶ厳しい感じのコードになります。
例えばこのUserListPageで、その非同期の処理が全部終わるまでは「Loading...」を出したいときも、その中のComponentのPromiseの依存を全部上に持ってこないとダメになってしまうところが問題です。
では、Suspenseはどう動くのかというところです。16でError Boundaryという機能が入りました。これはなにかというと、renderやライフサイクルメソッドでエラーが起きたときに、それを親のComponentのcomponentDidCatchというライフサイクルメソッドでキャッチして、エラー用の表示をするための機能です。これと同じ仕組みでSuspenseは動きます。
Error Boundaryの場合はエラーオブジェクトをキャッチするんですが、Suspenseの場合はPromiseがthrowされるので、それをキャッチして。この場合もTimeoutというComponentでキャッチして処理するみたいなかたちになります。実際にはthenableなオブジェクトだったら大丈夫です。
なので、例えばここでUserというComponentがPromiseをthrowしたら、Timeoutでキャッチして。Timeoutは、指定されたミリ秒が経つかPromiseがresolveするかどちらかで、Timeoutが先に来たらfallbackの表示を出し、PromiseがresolveしたらUserを表示する、みたいなフローになります。
というので、もう1個要素があります。Promiseのキャッシュをうまく扱うための仕組みが必要になるんですが、そのためのsimple-cache-providerというライブラリもReactが提供しています。
これはReactのリポジトリの中に入っていて、コード自体もかなり小さく、ほかの依存もないので、Reactのリポジトリの中にあるコードの中ではかなり読みやすいほうだと思います。
最近はキャッシュにLRUの実装が入って400行ぐらいになったんですが、それでもかなり短いので、簡単に読めるコードになっています。こいつはキャッシュのコンテナみたいなイメージです。
どのように遷移するかというと、最初はなにもないのでEmptyの状態になっています。そこでreadとかpreloadをすると、非同期処理のPromiseを作成します。そうするとPendingという状態になります。
このときにreadしようとすると全部Promiseをthrowするかたちになります。Promiseがresolveしたら、それをデータをキャッシュとして持って、それ以降は同期的にデータを返すという挙動になります。
じゃあどう使うのかというところなんですが、最初になにかキャッシュを作って、あとはResourceというPromiseの依存のもの。この場合だとfetchですね。このfetchをしたものとキャッシュを組み合わせてuserFetcherを作って、それをComponentのrenderメソッドの中で読みます。
なので、最初になにもデータがないときは、このFetcher.readでPromiseがthrowされるというかたちになって、レンダリングがここで止まります。
このComponentはPlaceholderというComponentで囲まれていて、その中でさらにReact.TimeoutというComponentがキャッチします。この場合だと、200ms以上だったら「loading...」という表示をfallbackとして出す、という実装になっています。
なので、こんな感じで状態を明示的には管理せずに書けるようになったり、このTimeoutのところを柔軟に制御できるようになるのがSuspenseの機能です。
Promiseがprimitiveなので、先ほどはfetchでしたが、当然Dynamic importなども同じ感じで扱うことができます。
ということで、ここからはデモをします。ネットワークに依存するのでうまくいくかわからないですけど(参考:React Suspense Demo)。
これは、APIのwaitやTimeoutの秒数を動的に切り替えられるような実装になっています。例えば普通に表示すると……。これはユーザーのstar一覧のリポジトリを取ってくるアプリなんですけど、今見たように「loading...」がチラって一瞬出てきました。
Suspenseを使うと例えば、1秒以上経ったら「loading...」を出したいということも可能です。逆にTimeoutまでにAPIのデータがloadしたら、「loading...」という表示は出ずに普通に出るという感じです。それで先ほどの問題が解決できます。
また、キャッシュに入っている場合、同期的にデータが返ってきます。だから一瞬で表示させることができるようになります。別のユーザーだと、またloadingが走るというかたちになります。
このように、非同期の更新処理の制御が柔軟にできます。
コードは、TimeoutのComponentがあって、expireしたら「loading...」と表示し、なければリポジトリのデータを出すようになっています(参考:GitHub - koba04/react-suspense-demo)。
このあたりはただAPIをfetchしているだけです。
さっきのcreateCacheなどを使ってfetcherオブジェクトを作成して、getApiDataとしてラップして、こいつをこのComponentのrenderメソッドの中から普通に呼んでいるだけという実装だけです。
もう1個は、これは画像を表示するケースです。普通に出すと、一瞬ガタガタっと表示されます。
これに対する解決方法としては、画像のサイズを固定するという方法を取ると思います。そうすると、画像のサイズは固定されますが、ネットワークが遅い場合だと、先ほどのように画像が徐々に読み込まれます。
例えば、回線を3Gぐらいにしておくとより顕著なんですけど、こんな感じにたぶん画像の読み込みが長くなります。これはたぶんみなさん許せないと思います。
ですので、方法としてはpreloadします。preloadすると、この場合は、画像の表示が全部preloadが終わった後に行われるので、一瞬でパッといい感じに出ます。
でも、今のなにも出ないのが嫌だという人は、プレースホルダー画像の表示もできるようになっています。例えばこんな感じにすると、プレースホルダー画像を表示して、loadが終わったら出すみたいなことが先ほどと同じ感じで出ます。
コードは先ほどとほとんどかたちは一緒になっています(参考:GitHub - koba04/react-suspense-demo)。ただ、今だとTimeoutのこのComponent自体が非同期のPromiseの投げるやつになっているんですけど。
例えばこの場合は、なにか別のただのComponentのwrapperがあって、この中にPromiseを返すComponentが入っているんですけど。こういうのって普通のPromiseのComponentサポートだと対応できないんですけど、そのへんもちゃんとできるようになっています。
では、ImageWrapperというタイトルを出したいと思ったら、その外側のTimeoutのさらに中でTimeoutでキャッチすることもできて、そうするとこいつが内側で処理するので外側まで伝播しなくなります。例えばこの「ImageWrapper」というタイトルはちゃんと表示されたまま、その中で画像のpreloadの表示がされるようになります。こんな感じですね。
という感じで、更新処理の制御はすごく柔軟にできるようになるというのがSuspenseの機能です。
最初のTime Slicingもそうなんですが、基本ネットワークが不便だったり、すごい低スペックな端末とか使っている人に向けての機能が、最近はすごく強化されているという印象です。
というわけで、今のは17でデフォルトで使えるようになる予定で、最終的なAPIはたぶんまだ変わります。Reactはcanaryというタグをつけていて、ReactのcanaryとReact DOMのcanaryとsimple-cache-providerをインストールすれば使えるようになっています。今のデモもGitHubにあって、Netlifyとかでデプロイしてるので、手元でも見れます。
そのほかでは、サーバサイドでSuspenseを動かすという実験とかも行われていて。クライアント側でReact DOMとかをまったく使わずに今みたいな表示が行われています。このへんはソースが公開されていないので、どういうテイストになっているかはわかりませんが。
あとは、ReactEuropeでは、先ほどのSuspenseをApolloのキャッシュと組み合わせるデモもあったりとかって感じで、けっこうそのあたりのエコシステムと組み合わせるということも盛り上がっている段階です。
というわけでまとめです。先ほどの例は、最終的なAPIではないので変わります。けっこうさっき見たみたいに、renderメソッドの中ってこれまでって「副作用みたいのは書くな」というのが鉄則というかベストプラクティスだったんですが、それを完全に覆してfetchしちゃうみたいな感じになっているので、Componentのあり方は変わってくるんじゃないかと思います。
あとは、先ほどのものを利用することでpreloadingをやったり、hiddenというpropsを使うことで、事前に裏側でメインのページを邪魔せずprerenderしておくみたいな、そういうのもできるようになったりします。
また、ReduxやStoreとどう組み合わせるのか、そうしたエコシステムとどう組み合わせていくかは今後も注目していくという段階です。
「最高! いつ使えるの?」というところで、一応予定としては2018年中に17がリリースされるイメージです。ただ、もう実際に使える段階になっていて、今はFacebookなどで実際に試したりしているような段階です。
ということで、Suspenseに備えていきましょう。ありがとうございました。
(会場拍手)
関連タグ:
2025.02.06
すかいらーく創業者が、社長を辞めて75歳で再起業したわけ “あえて長居させるコーヒー店”の経営に込めるこだわり
PR | 2025.02.07
プロジェクトマネージャーは「無理ゲーを攻略するプレイヤー」 仕事を任せられない管理職のためのマネジメントの秘訣
2025.02.06
落合陽一氏や松尾豊氏の研究は社会に届いているか? ひろゆき氏が語るアカデミアの課題と展望
2025.02.05
「納得しないと動けない部下」を変える3つのステップとは マネージャーの悩みを解消する会話のテクニック
2025.01.07
1月から始めたい「日記」を書く習慣 ビジネスパーソンにおすすめな3つの理由
2025.02.10
A4用紙を持ち歩いて殴り書きでアウトプット コクヨのワークスタイルコンサルタントが語る、2種類のメモ術
2025.02.05
エンジニアとして成功するための秘訣とは? ひろゆき氏が語る、自由な働き方を叶えるアプリ開発とキャリア戦略
2025.02.04
日本企業にありがちな「生産性の低さ」の原因 メーカーの「ちょっとした改善」で勝負が決まる仕組みの落とし穴
2025.02.03
「昔は富豪的プログラミングなんてできなかった」 21歳で「2ちゃんねる」を生んだひろゆき氏が語る開発の裏側
PR | 2025.02.04
能登半島地震で自宅は全壊、「これでどうやってDXするねん」 被災したサイボウズ社員と支援者らが語る災害支援のノウハウ
新人の報連相スキルはマネージメントで引きあげろ!~管理職の「他責思考」を排除~
2025.01.29 - 2025.01.29
【手放すTALK LIVE#45】人と組織のポテンシャルが継承されるソース原理 ~人と組織のポテンシャルが花開く「ソース原理」とは~
2024.12.09 - 2024.12.09
『これで採用はうまくいく』著者が語る、今こそ採用担当に届けたい「口説く」力のすべて
2024.11.29 - 2024.11.29
【著者来館】『成果を上げるプレイングマネジャーは「これ」をやらない』出版記念イベント!
2025.01.10 - 2025.01.10
片付けパパ対談【特別編】 整理術×行動術×メモ術で、仕事も人生も自在にデザイン!
2024.12.16 - 2024.12.16