2024.12.10
“放置系”なのにサイバー攻撃を監視・検知、「統合ログ管理ツール」とは 最先端のログ管理体制を実現する方法
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に備えていきましょう。ありがとうございました。
(会場拍手)
関連タグ:
2024.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
2024.12.09
10点満点中7点の部下に言うべきこと 部下を育成できない上司の特徴トップ5
2024.12.09
国内の有名ホテルでは、マグロ丼がなんと1杯「24,000円」 「良いものをより安く」を追いすぎた日本にとって値上げが重要な理由
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.29
「明日までにお願いできますか?」ちょっとカチンとくる一言 頭がいい人に見える上品な言い方に変えるコツ
2024.12.06
嫌いな相手の行動が気になって仕方ない… 臨床心理士が教える、人間関係のストレスを軽くする知恵
2024.12.10
職場であえて「不機嫌」を出したほうがいいタイプ NOと言えない人のための人間関係をラクにするヒント
PR | 2024.12.04
攻撃者はVPNを狙っている ゼロトラストならランサムウェア攻撃を防げる理由と仕組み
PR | 2024.11.22
「闇雲なAI導入」から脱却せよ Zoom・パーソル・THE GUILD幹部が語る、従業員と顧客体験を高めるAI戦略の要諦
PR | 2024.11.26
なぜ電話営業はなくならない?その要因は「属人化」 通話内容をデータ化するZoomのクラウドサービス活用術