1. kkeethのエンジニア雑談チャンネル
  2. No.119 朝活「なんでコンポー..
2022-10-26 30:06

No.119 朝活「なんでコンポーネントに副作用があんだよ! 教えはどうなってんだ教えは!」をダラダラ読む回

はい.第119回は


なんでコンポーネントに副作用があんだよ! 教えはどうなってんだ教えは!
https://zenn.dev/uhyo/articles/react-use-rfc-2


を読みました💁

またまた uhyo さんの記事になりますが,前回の「最速攻略!React の`use` RFC」という記事の続編でもある本記事は uhyo さんのご考察がメインとなっており,個人的にはこちらの方が興味深く面白みもあり,また読み応えもありました.ぜひ皆さんも読んでみてくださいー!


ではでは(=゚ω゚)ノ


  • React
  • use RFC
  • コンポーネントの冪等性
  • Suspense
  • Promise はデータ
  • リソース
  • イミュータブル
  • 外部の責任境界
  • useMemo
  • 非同期処理
  • React Server Components
  • useState


See Privacy Policy at https://art19.com/privacy and California Privacy Notice at https://art19.com/privacy#do-not-sell-my-info.

00:05
はい、10月22日土曜日ですね。 中国朝九条もありました。
なんでしょう、休みの日なんで。
ダラダラしたいんですけども、やっぱりこの土日もしっかり勉強もしたいなと思うところで、
朝一、今日もまたukioさんの記事を読んでいこうと思います。
はい、おはようございます。ユメミのkeethこと桑原です。
では本日も朝活を始めていきたいと思います。
はい、先日のukioさんの最速攻略リアクトのユーズRFCという記事を読んで、
その後にユーズRC本体の、RCのプレリクエストですね、自体も読みました。
で、そこからさらにですね、ukioさんが書かれていたなんだっけ、
あれですあれです、なんだっけ、サスペンスですね。
リアクトのサスペンスの非同期処理を手書きするハンズオンだったっけ、
そのタイトルのハンズオンの記事を全て書かれていて、
それも一回手を動かしてみたんですよね。
改めて感じたのは、リアクトってむずいなって思いました。
とてもですね、開発者フレンドリーで、設計もすごく練られていて、
かついろんなエコシステムとかの連携とかも加味したり、
フォロントエンドの人たちの開発の責務だったりとか、
設計、いろんなアプリケーションに特化したような、
寄り添うことができるような設計になるようにっていうところが考えられていて、
リアクトというライブラリーというのの素晴らしさを本当に強く感じています。
これ自体は別に、書いてたり読んでたり、
いろんなものに目にして実感としてあるんですけど、
ただやっぱり、ちゃんと作ろうとすると、
リアクトの思想レベルというか、
彼らが見ている世界観というところに乗っからないと、
やっぱりリアクトのパワーとか効果というのは
フルに活用できなかったり、実感できないなという感じはするんですけど、
とはいえやっぱり、ちょっとずつやっぱりリアクトも複雑度が増していたり、
機能がだんだん増えていったりしているので、
ちょっと難しくなってきた、大きくなってきたなという感が正直あります。
ただその代わり、いろんなアプリケーションとか、
いろんな状況に対応できるようなものがあるというのは、
本当に素晴らしいお話でいいんですけどね。
リアクト専門家の人、極力リアクトエンジニアが欲しいなというぐらいの感じです。
まあまあ余談でした。
じゃあ今日は、そんないろんなものを読みつつ、
最速攻略リアクトのUserRFCの続編ですね。
なんでコンポーネントに副作用があるんだよ、教えはどうなっているの、教えは?
という面白いタイトルの続編が出ていたので、
これも一緒に読んでいこうと思います。
一旦この最近のリアクト周りのお話は以上にして、
次はまた別の本を読んでいこうかなと思ったりしています。
はい、じゃあいきたいと思います。
みなさんこんにちは。
先日公開した以下の記事は多くの方にご覧いただきありがとうございます。
この記事に対して多く見られた反響の一つは、
コンポーネント内にUseフェッチノートIDという
非同期処理を行うコードが含まれていることに対する違和感だと言っています。
03:00
コンポーネント内にUseという非同期処理を行うコードが含まれるというところに
違和感があったらしいですね。
僕感覚が狂ってるかわかんないですけど、
こういう書き方するんや、くらいの感覚でした。
しかも非同期処理を発火するとなれば、これは明らかに副作用ですから、
このような処理はUseエフェクト内で行うはずでした。
ご存知の方も多いと思いますが、
リアクトにおける関数コンポーネントはしょっちゅう呼び出されるものです。
そこに副作用が書いてあるとなると、
必要以上に非同期処理、フェッチとかの場合は
HTTPゲストが発生することになってよくありませんと。
そこでこの記事ではなぜこのように
コンポーネントの中で副作用を直接呼び出しても良いと
リアクトチームが考えているのかに分析していきたいと思います。
なるほどね、そういう思想でこのタイトルだったんですね。
前回の記事は事実を中心にお伝えしましたが、
この記事では筆者の考えや想像を多文に交えていきますので、
足からずご了承ください。
サスペンスフォーデータフェッチング時代のコンポーネント設計だそうです。
まず、このようにコンポーネントがないから
フェッチに類するものが直に発火するというのは、
UseのRFCで初出のアイディアというわけではありません。
リアクト18でサスペンスフォーデータフェッチングが出た時点で
存在したアイディアがUseのPromiseを直接取り扱えるという性質によって
より強調されたものだというふうに解釈ができますと。
そういう解釈があるんですね。
なるほどでした。
まあまあでもそうですね、エンジニアはPromiseと戦うんですけど
Promiseを使っても面倒くさいんですよね。
なのでそれをリアクトがうまいことメンテナンスしてくれるようになる。
このAPIを乗っかればいいというのはすごくありがたいんですよね。
というところでした。
中核となるアイディアは取得された生のデータではなく
非同期に取得されるという文脈も含めたデータを
プリミティブなものとして取り回すということです。
タイプスクリプトの言葉で言えばTを取り回すのではなくて
Promise-Tですね。
Generics-Tを取り回すということです。
Tを取り回すのは旧来のやり方で、この場合はローディングなどのフラグを
セットで持ち回る必要があります。
非常に原始的な例としてはソースコードが出ていますね。
UseStateで初期値トゥルーを入れておいて
ローディングとセットローディングみたいになるので
もう一個UseStateでGenerics-Note、PipeのUndefinedで
初期値はUndefinedですね。
Noteというのは今からAPIをコールして受け取るデータだと思います。
それをConst-NoteとSet-Noteというので受け取っています。
UseEffect内でFetch-Noteというのをやって
適当なIDで振って、前のレスポンスを
コールバック関数のSet-Noteというのがあるので
Set-Noteでセットしていきます。
UseEffectの引数、代入引数ですね。
代入引数にIDをして仕上げば入れていきます。
ID変更すればコールできます。
そういう感じですね。これが原始的な例です。
懐かしいなと思います。しかしPromise-Tというのは
内部的に読み込み済みかどうかという情報を持っていますから
Promise-Tをそのままデータとして扱えば
06:02
ローディングを別に持つ必要は要らないはずです。
Promiseをデータとしてみなすようになると
こうなります。
Const-Note-Promise-Colon-Promise-Noteですね。
Generics-NoteイコールFetch-NoteのIDです。
というふうなやり方ができると。
ここで一つ問題があります。
それはPromiseが読み込み済みかどうかの情報を持っているとは言ったものの
JavaScriptの言語仕様ではその情報を直接
同期的に参照することができず
Promiseから情報を取り出す方法が前及び
内部処理で前を使う構文であるawaitしかないということです。
リアクトの関数コンポーネントは
Async関数ではないのでそのままPromiseの
情報レンダリングに使うことができませんでしたということです。
リアクト18が出た当時の
アイディアは生のPromiseに情報を付加したラッパー
リアクト公式はこれをよくリソースと呼んでいます。
用意してそれをコンポーネントで使用するというものです。
サスペンドという概念をリアクトに導入することで
ローディングという状態をコンポーネントが明示的に扱わなくて良くなります。
というところですね。
コンストノートリソースコロンリソース
という型でGenericsノートですね。
イコールフェッチノートのIDです。
これはノートリソースが読み込まれていなかったら
サスペンドするというところで
コンストノートの型がノートですね。
イコールノートリソース.ゲットというようなミソッドを実行するみたいなんですね。
そしてこのようなリソースの管理をうまくやってくれるものとして
データフェッチングライブラリーがフィーチャーされることになります。
UseSWRやUseQueryなどを使っていると
リソース扱っている印象がありませんが
リソースってのはかけがっこリソース付いているから
リアクト公式がよく述べているリソースっていうやつですね。
生のプロミスに情報付加したラッパーのことですね。
リソースを扱っている印象がありませんが
いわゆるレンダーアズフェッチパターンを用いる場合は
そのリソースを明示的に扱う場面が出てくるでしょう。
UseRFCではこの方向性をさらに進めて
リアクト本体がもうちょっと頑張れば
リソースオブジェクトという中間層をなくして
プロミスを直に取り扱えるのではないかという
アイデアが提唱されます。
2つ目のツールがUseになります。
先ほどのやつですね。
先ほどのフェッチノートのところですけど
もしサスペンドされていたら
ノートリソース.getメソッドを使われていたんですけど
それがUse括弧ノートプロミスみたいな感じになる感じですね。
以上がUseが直にプロミスを受け取るという
APIの設計の背景です。
Tとローディングを別々に持っているよりは
またリソースがプロミスへの転換についても
設計的な面での進化があります。
というのもリソースは非常にミュータブルなデータになります。
読み込み状態が変わるとリソースの状態が変わり
挙動が変わります。つまり上の例で言えば
ノートリソース.getメソッドの挙動というのは
09:00
それをいつ実行したかによって変化するということです。
この意味でミュータブルなデータを取り扱うのが
難しいとされるリアクトのデータフローの中で
リソースは異端の存在です。
リアクトはコンポーネントの適当性を重視している
というところなので、いわゆるイミュータブルです。
恐ろしいとされているリアクトのデータフローの中で
リソースは異端の存在だったらそうですね。
一方でプロミスはイミュータブルなデータです。
プロミスも内部状態が変化しますが
データの読み出しは全を行うものしかありません。
そのためプロミスがどういう内部状態の時に全を読んだとしても
読み出しをする結果は時間の差こそあれ同じです。
この点によってUseのノートプロミスは
プロップやステート以外の要因で
レンダリング結果は変化しないという
リアクトコンポーネントの要件を満たしているとわかりました。
リアクトコンポーネントの適当性です。
前述で述べた説明したように
Useを使う世界ではもはやプロミスはデータであると考えましょう。
そうすればフェッチノートというメソッドは
プロミスというデータを取得する関数であると再解釈できます。
実際リアクトコンポーネントは
プロミスをただアウェイトや全されるだけの存在ではなく
それ自身をデータとして扱う方向性を推進しているように見えます。
それを裏付けるように
Useのアレシティテもプロミスオブジェクト自体を
キャッシュして使えますみたいに言及されています。
これはちなみにアレシティ本体を読んでみてください。
僕の浅川さんにも読んだので
もし興味ある方は聞いてみてください。
本体のやつを読む方が絶対いいと思います。
フェッチノートは一見すると
httpリクエストを発火させることが主な責務のようですが
これを文字通り副作用であると解釈すれば
フェッチノートの主な責務はIDを受け取って
プロミスというデータを返すことであると考えられます。
データがサーバーサイドで変化する可能性は一旦横に置いておくとしましょう。
そうするとフェッチノートというのは
データが返ってくるという性質を持つことになります。
プロミスはオブジェクトなので
厳密な投下演算子の意味では同じにはならないかもしれませんが
データの意味としては同じものが返るはずです。
雑な言い方をすれば副作用はあるけど
それを除けば参照投下性が満たされていくことになります。
UseのRFCでは
リアクトコンポーネントが満たすべき性質として
べき等という言葉が仕切りに使用されます。
何回呼び出しても1回だけ呼び出したのと同じ結果になるということを意味しています。
リアクトコンポーネントは純粋とか参照投下とか
いろいろ言われますが
べき等というのはかなりリアクトが求める実装に近い表現です。
なぜならリアクトではコンカレントレンダリングの導入以降
コンポーネントを1回レンダリングする間に
関数コンポーネントが複数回呼び出されるという挙動が
あちこちで取り入れられており
リアクトコンポーネントはそれに耐えなければいけないからです。
その理由は
複数回呼び出された複数回複作用が発火してしまい
べき等ではなくなってしまうからだとおっしゃっています。
12:01
そもそもリアクトコンポーネントにべき等性が求められる理由は
リアクトが期待通りに動作するためと
リアクトに期待する動作を伝えるための2つの側面があります。
後者は宣言的弱いという特性上
コンポーネントがべき等でなければそもそも要求が明確とは言えません。
べき等でない挙動が組み込まれたコンポーネントは
宣言的でないリアクトの内部機構に何かしら依存しています。
逆に言えば複作用があったとしても
べき等であればセーフと考えられないこともありません。
例えばコンポーネントがフェッチノートを呼び出して
複作用が発生するものだとしても
べき等であればリアクトコンポーネントの質としては問題ありません。
そしてフェッチノートの結果を2回目以降キャッシュしておいて
フェッチノートを何回呼び出しても実際のリクエストが1回しか実行されないとすれば
フェッチノートはべき等になりますから
リアクトコンポーネントもべき等になります。
なおかつキャッシュしているので高速に返してくれるということで
かなりいい話ですね。
この考え方ではフェッチノートはリアクトコンポーネントからの呼び出しに耐える
べき等な実装として用意しておくことになります。
これがなんでコンポーネントに複作用があるんだに対する
答えの半分になりますと言っています。
今までの話は全部理解すると
確かにコンポーネントに複作用があるというところの
理解ができましたね。
一気に読んでいるし、本当は一個一個ちゃんと
理解してから読むのがいいと思いますので
一時停止してもらって咀嚼して読んでいただくのがいいと思います。
僕も後でもう一回改めて咀嚼しつつゆっくり
読み直したいと思います。
続いてリアクトと外部の責任協会に入ります。
答えの半分と言いましたが残りの半分はどこにあるんでしょうか。
弊社はそれにはリアクトと外部の責任協会を考えれば
見出せるというふうに思っています。
これは今まで見てきた通りリアクトコンポーネント側から見て
フェッチノートに求められることは
プロミスというデータを返すことおよびべき等であることです。
ポイントはプロミスはもはや単なるデータとしてしか見られておらず
その場で非同期処理を行うことは必ずしも
フェッチノートに求められていません。
それはフェッチノート内部の実装詳細の話であり
ユーザーであるリアクトコンポーネント側から興味のないことです。
非同期処理を行えばどうでもよくて
プロミスデータは返ってくるそしてでべき等であるということさえ
満たしてあれば何でもいいということです。
別の言い方をするとポイントはコンポーネントがフェッチノートを読んだからといって
必ずそのタイミングでリクエストを発火しなければいけないとは限らない。
フェッチノート側の好きなタイミングで発火すればいいということです。
むしろキャッシュだけに留まらず
例えば不思議な先読み機構によって
フェッチノートを初回に呼び出す前よりも
データが準備されていたとしても問題はまたかりません。
とにかくさっきのプロミスデータを返すことと
べき等であるということさえ満たせれば
フェッチノートを使うコンポーネント側の責務も
HDBリクエストを発火してノートのデータを取得して
それを表示するではなく単にノートのデータを表示すると解釈すべきです。
みんな大好き。関心の分に従います。
コンポーネント側は
15:00
ノートを得るのに時間がかかるかもしれない。
プロミスが得られることは知っていてもかまいませんが
それをどのように取得するのかはコンポーネント側が知るべきではありません。
知らなきゃいけない。
依存度が高く分離ができないと言う。
一応ノートを得るのに時間がかかるかもしれない。
だからプロミスが得られる。
それは知っててもいいけど
どのように取得するかはコンポーネント側が知るべきではない。
故に筆者の解釈では
するとコンポーネントと副作用が直接的に 結びつけられたように見えるものの
実際はむしろ逆だと言ってます promise というよく抽象化されたデータ
をインターフェースとして実際のフェッチ 処理をリアクトコンポーネント
の関心からひやかしたとみなすことが できますそうなると問題はフェッチ
ノードという名前にあるのだという 気持ちになりますフェッチとついた
名前はwhat was the fetchの影響もあり 実際にリクエストを発生させる
ことを強く想起させますですから どこからかはよくわからないけど
データを取ってくるというイメージ に適した動詞をフェッチの代わり
に当てがって生やされるのがいい のかと思いますなんかそういう
ライブラリー出てきたら面白い ですね最終的に問題は名前にな
っちゃいますあと名前に関して 僕らがもうすでに概念を理解して
しまっているせいでなんとなくの イメージとか印象もセットでついて
しまっているというのが今の課題 だと思うんですねただそこを別に
自分の中で分離できるようになれば フェッチという名前でもいいっちゃ
いいですけどフェッチというと なんとなくhttpリクエストを想起
しますよねそもそもJavaScriptの フェッチAPIがそういうAPIですから
ねはい続きますまとめるとなんで コンポーネント副作用があんだよ
に対する問半分の答えは副作用 はプロミスという抽象の向こう
に隠されたのでコンポーネント から見れば副作用ではないという
ふうになりますとははははそういう 答えかなるほどですねまとめる
って言って僕は理解できなかった のでなるほどって今なったんですけど
はいじゃあえーと続いてユーズ カッコのカッコアロープロミス
ではダメなのかとユーズの今の デザインではユーズカッコプロミス
という形でユーズを使いますで前回 の記事に対する反響を見てみる
とそうではなくユーズカッコの カッコアロープロミスっていう
ことですねあのアロー関数で実行 しゃダメなのかという意見も見
られましたでユーズカッコプロミス の問題はプロミスが無駄に何回
も作られてしまうということです でユーズカッコフェッチノート
idだとコンポーネントの再レンダリング 時にフェッチノートidが再び呼ば
れることになりますあそうねさらに コンポーネントがサスペンドした
後に再レンダリングする場合も コンポーネントが関数として再度
呼ばれますから一回の連打余裕 なくてフェッチノートが呼ばれる
ことになるわけですとはいだから くそフェッチノートがべき党
であることが重要になりますとこの 話について考察するために具体的な
コンポーネントでユーズの動きを 復習しましょう最初の例をちょっと
簡略化した次のコンポーネント でユーズの挙動を追ってみます
とはいまだ関数コンポーネント があって名前はファンクションノート
で引数にidを受け取りますとでその idをベースに一行目でコンストノート
イコールユーズカッコのフェッチ ノートのカッコidでノートのデータ
18:04
を取得してそれをノートという 変数で受け取るとでそのリターン
の中にjsxでそのノートのタイトル とかボディとかが連打にされる
という感じですでここでこのノート コンポーネントですねノートコンポーネント
をどっかのところにノートスペース のidイコール右表みたいなところ
でレンダリングしたとしましょう まあどっかに配置したということ
ですねノートのレンダリングが 一回完了するまでの手順は次の
通りになりますおお長いなまあ 一個一個いきましょうまずノート
っていうコンポーネントが関数 として呼び出されますでフェッチ
ノート右表が呼び出されてプロミス が返るこれをプロミスワンとしましょう
でユーズにプロミスが渡された のでユーズの内部処理でプロミス
ワンがスローされサスペンドが まず発生しますリアクトはノート
のレンダリングノートコンポーネント のレンダリングを中断しプロミス
ワンが解決されるのを待ちます プロミスワンが解決されますする
とプロミスワンの結果はリアクト 内部に保存されますとリアクト
はノートコンポーネントのレンダリング を再開しますノートコンポーネント
が関数として再度呼び出されます そうね一回サスペンドしてるとこ
ですからねフェッチノート右表が 呼び出されてプロミスが返ります
これをプロミス2としましょう ユーズにプロミス2が渡された
が今回はサスペンド明けの再試行 なのでプロミス2が無視されます
とここ一応太字になってます 代わりに5ですねプロミスワン
が解決されてプロミスワンの結果 はリアクト内部に保存される
というところの処理ですけどここで 保存されていたプロミスワン
の結果がユーズの返り値として 使用されますと重要なのはプロミス
2の結果じゃなくてプロミスワン の結果になるむしろプロミス
2が無視されるところが重要です とノートコンポーネントが無事に
返り値を返しそれをリアクトが レンダリングしてノートコンポーネント
のレンダリングが完了すると このようにユーズの機構ではユーズ
に渡されたプロミスの結果が出揃う まで関数が繰り返し呼び出され
サスペンドせずに成功したらレンダリング が完了するという流れになります
こうすることで最後の1回のノート コンポーネントの呼び出しでは
ユーズに渡したプロミスの結果が 同期的に取り出されたように見える
というトリックになっています 見かけ上は同期的な感じに見え
ますねこうやって見ると しかしよく見るとわかるように
上のストップ8ではユーズにプロミス 2が渡されたがそれは無視され
代わりにプロミスワンの結果が 返されるという挙動になっている
ことがわかります これが許されるのがコンポーネント
に適当性があればプロミスワン の結果とプロミス2の結果は同じ
はずだからです 結局は適当性が他の保たれている
ところが重要だということです しかしよく見るとわかる今言いました
つまりレンダリングを試みるサスペンド 再挑戦というサイクルの間は実際
に有効なプロミスは最初にユーズ に渡されただけのものであって
2回目以降にユーズに渡したプロミス っていうのは実は使われていない
んですよってことです このことがユーズ括弧の括弧アロー
プロミスを求める理由となります ユーズに関数を渡すようにすれ
ば最初の1回だけ関数を呼び出して もらって2回目以降呼び出さない
ようにすることで無駄なプロミス が作られるのを避けられます
さらに依存のリアクトのAPIにも 似たような状況になっているもの
があります それはユーズステート の引数には初期値を渡しますが
21:05
その初期値が有効なのはコンポーネント が最初にレンダリングされたとき
だけで2回目以降のレンダリング ときを渡された値は無視されます
よってことですね 前回の記事で述べた通りユーズ
ステートはコンポーネント単位 の記憶領域にデータを保存する
のに対してユーズというのはレンダリング 単位の記憶領域にデータを保存
するという違いがあります どちらも記憶領域は初期化する
際は引数で渡られた値を使い2回 目以降では引数は無視されるという
意味で全く同じですよと 実はユーズはユーズステートに
近いフックだったのです 話を戻すとユーズステートの
引数は実は関数にできます ユーズステートで括弧でプリミティブ
な値でもいいですしオブジェクト でもいいですけどユーズステート
の括弧で括弧アローを値みたいな ことも一応できますと
そしてユーズとユーズステート が近いことを踏まえるとユーズ
をユーズ括弧の括弧アローをプロミス という関数にするというAPIにして
余計なプロミスが作られないように することは可能だと考えられます
ただしそうすべきかどうかは別の 話です
皆さん上のユーズステートの例 だとユーズ括弧配列ですね
空配列が初期値に指定されています ユーズステート括弧ですね
あとユーズステート括弧のアロー の仮括弧ですね
どちらを選択しますかと
筆者は前者の方ユーズ括弧の仮 括弧ですね
を選択します
なぜならレンダリングのために 無駄な仮括弧を作られて捨てる
くらいは全く問題にならないオーバーヘッド と思うからです
そもそも例であればアロー括弧 仮括弧ですよね
もう新しい関数オブジェクトを 一つ作る構文であるのでどちら
もオブジェクトを一つ作っていく のでは変わりはありません
ああ確かにそういう観点でいけば 一緒だと思いますね
では次の例だとどうでしょうか と
ユーズステートの括弧でカルキュレイト ラージオブジェクト括弧みたいな
メソッドですね
計算処理した後のオブジェクト が返ってくるけどそれが結構で
かいやつらしいですね
という場合とユーズステートの 括弧の括弧アローでカルキュレイト
ラージオブジェクトメソッドを 実行した結果です
これだと後者を選択した人が多い のではないでしょうか
その理由はカルキュレイトラージオブジェクト 括弧がオーバーヘッドでかそう
だからです
こうなるとユーズステートに関数 渡すかどうかはオーバーヘッド
によって決まりそうです
これはつまりユーズステートに関数 渡すことはある種の最適化である
ということも言えます
ユーズとユーズステートが非常 に似ていることから考えると
ユーズの括弧の括弧アローをプロミス っていうのをやりたいのであれば
その理由も最適化でなければなりません と
なりませんなのかな
まあでも似てるから寄せるっていう のだったら全然いい話だと思う
けど
僕なんかなりませんって思わなかった けどな
ただ最適化の理由で関数化する っていうのは別にいい話だとは
思いますけど
逆に言えば無駄なプロミスが作 られても問題ない
特に無駄なリクエストが発行された りしないというのであれば
わざわざ関数の形にせずにユーズ プロミスであっても別に問題ない
ということになりますと
ここは同意ですね
そもそもこれまでに主張した通り
リアクトコンポーネントがない からフェッチノートIDを呼び出
している時点で
フェッチノートにリクエストの 発火という副作用を期待すべき
24:00
ではそもそもありませんと
リアクトコンポーネント側から フェッチノートがいつどのように
データを取得するのは興味のない ことです
そう考えるとユーズの括弧プロミス ですね
括弧アロープロミスというAPI にしてしまうと
一回しかリクエストが走らない ようにリアクトが制御してくれる
という期待を与えてしまいます
そのような期待はコンポーネント にリクエスト発火の責務を持た
せるという良くない設計を前提 として
そのような期待はコンポーネント にリクエスト発火の責務を持た
せるという良くない設計を前提 とし
そちらにユーザーを導いてしまいます
そのため筆者はユーズの括弧の 括弧アロープロミスよりも
ユーズ括弧プロミスの形の方が むしろ良いんじゃないかという
ふうに思ったりしていますよ ということですね
ああまあでもそうですね今回の RFCも踏まえつつ
そういう思想に乗っかるんであれば
むしろユーズ括弧プロミスの方が 認知という意味ではそうかもしれない
ですね
そういえば括弧アロー処理の形 のAPIはユーズメモもありました
ユーズメモは依存範囲列が変わらない 限り関数を呼び出さずに
以前の結果を使いましてくれる という機能で
これも最適化のために使うもの ですと
ユーズメモに関しては公式ドキュメント に将来のリアクトのバージョン
では依存範囲列が変わった以外 の理由で
ユーズメモのキャッシュが破棄 されるケースが出てくるかもしれない
という旨の注意書きがあることで 知られています
現行のリアクトバージョンでは キャッシュが破棄されるケース
はなさそうですけどそれは将来 に渡る保証はありません
っていうことですね
ユーズも同じように最適化という 立ち位置になることを考えると
ユーズの括弧アロープロミスという APIだったとしてもその関数が将来
渡って最初の1回しか呼ばれない という保証はおそらくされない
と思います
初期のバージョンではそのような 挙動をするかもしれないが
将来に渡って保たなきゃならない 保証をわざわざ増やす理由がありません
ということでヒッシャンの考え としてはユーズの括弧アロープロミス
というAPIになったとしてもそれ にロジック上の要件を任せられない
ので
それならはより簡潔なユーズ括弧 プロミスでよいと思いますよという
ふうにおっしゃっています
続いてユーズメモだとダメなの という話をしています
ユーズメモの話題が出たので少し 考えてみましょう
ユーズに使われないプロミスを 渡したくなければプロミスをユーズ
メモでキャッシュするという手 が考えられそうです
つまりこうですというソースコード で出ていますね
同じように関数コンポーネント ノートが使われています
ファンクションノートの引数ID を受け取って
1行目でユーズメモの括弧のアロー フェッチノートIDで第二引数にも
IDが指定されています
それをノートプロミスという変数 に受け取って
そのノートプロミスをユーズに掛 けてあげてその結果をノートという
変数に受け取ります
最後そのノートをレンダリング JSXでレンダリングするというところ
ですね
これは一見良さそうに確かに見え ますね
メモを使ってメモ化しているということ ですね
だけど残念ながら意味はありません
なぜならユーズメモはコンポーネント 単位の記憶領域にキャッシュを保存
しますけど
コンポーネントが初回レンダリング でサスペンドした場合はコンポーネント
単位の記憶領域が破棄されて しまう
というのが意味ないというところ のコンセプト
ユーズはそれとは別のレンダリング 単位の記憶領域をわざわざ導入
することによって
サスペンド前とサスペンド後の 思考でデータを置き渡しています
サスペンド時にわざわざコンポーネント 単位の記憶領域を破棄する理由
27:00
筆者のリアクト力が足りないので 説明できません
理由がわかる方はぜひコメント でご教授ください
サスペンド時にわざわざコンポーネント 単位の記憶領域を破棄する
コンポーネント単位の記憶領域 を破棄する理由がわからない
と思っていてもいい感じはします けどね言われてみれば
サーバーコンポーネントで思い っきりDBにアクセスしてるじゃん
っていうのが次のセクションです
ユーズのRFCよく見るとサーバー コンポーネントはasyncにできる
という文脈でこんなコードが出て きます
またソースコードですね
asyncのファンクションでnノート っていうような関数コンポーネント
です
引数にidとiseditingっていう2つの 引数を受け取ります
1行目でawaitのdb.post.getで引数 のidを渡してあげてます
それの結果がノートが返ってくる のでconstノートっていう変数で
受け取ってそのノートをjsxレンダリング でレンダリングしています
途中でiseditingっていうのがある のでiseditingの場合はそのノート
エディターっていうコンポーネント をセットするかもしくは塗る
だよっていう感じに条件分岐で 使ってます
こういうことを関数コンポーネント サーバーコンポーネントのほう
でできるってことですね
これは明らかにコンポーネント の内でデータベースにアクセス
する行動に見えます
本当に見えますね
db.postって書いてますからね
ここまであからたまにやられる と責務の分離がどうとかそういう
言い訳が通用しません
確かに
これについての弊社の理解はサーバー コンポーネントは従来のリアクト
とはメンタルモデルが大きく異なる
サーバーコンポーネントはクライアント 側のリアクトとうまく統合された
テンプレートエンジンみたいな ものであり再レンダリングという
概念がそもそもないので問題ない と考えています
というわけでユーズではなくアウェイト が使えるという点からもわかる
通りサスペンドという概念もそも そもありません
だからここは非同期処理をコンポーネント からあからたまに呼び出しには
問題ないです
だって便利だし
そうなんだよね結果便利なんですよ ね
はいではまとめです
なんでコンポーネントで副作用 があんだよ
教えどんな点だ教えはっていう 給意に対してアンサーは
副作用があってもべきとならまあ 大丈夫
というか副作用はリアクトの外の 世界に追い出したので副作用と
見出さなくてもよいよということ を出そうで
はい以上がこの答えになりました ということでした
はいということでこの記事は以上 になります
はい前回の記事またRFCも含めて いや本当に学びになりましたし
この3つの記事読むだけでリアクト 力めちゃくちゃ上がるなって感じ
がしました
これ一個一個ちゃんと咀嚼詞なんか 読めるだけでリアクトの設計と
思想と内部的なところまで理解 ができるので
改めてしっかり読んでいきたい なと僕は感じましたね
はいというところで今日の朝方 以上にしたいと思います
はいやっぱりリアクトでもやっぱ 難しいなって印象はすごくあります
けど
やっぱり理解していくとよく設計 されて美しいなっていう風に感じる
ところも正直あるので
難しいならではでも表現力豊か になりますし
いろんなアプリケーションでも ちゃんと耐えうる
大規模な開発とかもしやったとして もそれに耐えうる設計になっている
っていうところの設計の美しさを 感じるためにもやっぱり
しっかり理解していきたいなという ふうに思いました
リアクト力って必要だと思うので リアクトの筋力平均トレオが必要
だなっていう風には感じました ので頑張っていきたいと思います
では今日の朝方これで終了したい と思います
30:01
お疲れ様でした
30:06

コメント

スクロール