insideはちょっとだけお仕事から離れて(ガチ)アイスブレイクです。いうFluxフレームワークを参考に、「書きやすさ」と「現代的な機能の採用」の二点を重視してTypeScriptでフルスクラッチしました。Delir(デリール)というパフォーマンスセンシティブなプロダクト内で1年間、APIとパフォーマンスの調整を行って開発されています。actionには非対応どういう経緯で生まれたかFleurは、FluxUtilsで実装されていたDelirのより堅牢な移行先を模索する中で生まれました。設計面からFluxibleが移行先として一番良い(※)と考えていましたが、Fluxibleの型定義は存在せず、自力で型定義を行うにも型推論と相性の悪いAPIだったため、「じゃあ一発自分で作るか〜」という気持ちを固めました。Hooksを参考に、どういうAPIならよりシンプルかつ堅牢で、プロダクションでも使えるコードを書けるかという方向で設計を研ぎ澄ませていきました。Fleurの生まれ元のプロダクトのDelirでは、JavaScriptのメインスレッド上に「映像のレンダリング処理」と「UI処理」が同居しているため、パフォーマンス方面で問題がないようにチューニングしています。redux-thunk」を使うことも検討しましたが、当時はまだimmer.jsを認知しておらず、かつDelirのエンジン側APIの独立性を担保する必要があったため、エンジンのAPIを変更せずに採用できるフレームワークとしてFluxibleが一番良さそうという事になっていました。(プロジェクトスキーマがネストしたクラス構造に依存していて、イミュータブルなオブジェクト郡に変更するのが困難だった)最近になってimmer.jsが変更されたオブジェクトのプロトタイプを保持するようになったようなので、今なら違うやり口もあるかもしれません。(でもフロントエンドでJSON以上のオブジェクトをStateに使うのはやめましょう。Storeの定義を見ていきましょう。Action自体の定義とその型定義が一体化しているので、記述が散らばらず、一つのActionの定義のために複数箇所のコードを書かないといけない、という工程が減らされています。Identifierと呼んでいます。Operationsは、最初から非同期処理に対応しています。そのため、thunkかsagaかという選択が必要ありません。Storeは、listen関数と.updateWith()が特徴です。Identifierを元にそのActionのハンドラの引数型を推論してくれます。typeとそのハンドラメソッド名を指定する形でしたが、よりシンプルで型推論に優しいコードを考えた結果このようなAPIになっています。いう関数でも作れるようになっています。SSRを行っているサービスのプロダクションコードを元に設計しています。私が調査した範囲だと、現在Reactの周辺に存在するルーターライブラリは、概ね共通して「APIリクエストについてのベストプラクティスが存在していない」という問題を抱えているように見受けられました。Hubでもそのあたりはかなり汚い自前実装を行っています。その点については、Fluxibleのfluxible-routerはかなり理想的な解に近いAPIを持っていたので、Fleurもそれを踏襲しました。importの対応がFleurでは実装されています。importへの対応が出来ます。Viewの最低4要素が必要です。多いなぁという気がしますが、中大規模アプリを書くなら、割とペイできるんじゃないかと考えてます。CreatorがActionsとOperationsに分離された感じです)おすすめのディレクトリ構造は以下のような感じです。store.tsここからは順番にサンプルコードを添えて流れを説明していきます。ActionsFleurでのActionの実態はただの型ストアで、何もしない関数です。action()関数はpayloadの型を引数に受け付け、実際には呼べない関数を返します。Identifierと呼んでおり、dispatch時に関数インスタンスとの厳密等価性を見てStoreのリスナを発火します。(JavaScriptトリックです)StoreStoreではlisten()関数を使い、どのActionに対してハンドリングするかを指定します。Storeの特徴は2つあります。Identifierにより、payloadの型が推論されます。this.updateWith()。このメソッドの実態はimmer.jsのラッパーなので、draftに対してミューテーションを起こしても、stateの更新はイミュータブルになります。}のように波括弧を付ける必要はありません。2)ため、単一のハンドラ内で複数回.updateWith()をコールしても、UIへのブロックは最小限になります。listen関数がどのようにActionとハンドラを紐づけているかについては、以下のコードをご参照ください。一回のdispatchで複数のStoreの状態が変化する場合でも、それらを取りまとめてバッファリングされます。SSR時はバッファリングを行わず、同期的に処理します。OperationsこれはFleur独自の名称ですが、APIとの通信や副作用を起こす層です。redux-thunkなどに相当します。エンティティの正規化も、正規化処理を関数に切り出したりnormalizrを使ったりしてこの層でやると取り回しが楽です。getStore}が渡ってきており、operation内からStoreの状態を参照することも可能です。「Storeにあるユーザーの認証情報がAPIリクエスト時に必要」という場合に便利です。operations()関数自体は型推論のためだけのもので、特に何もしていません。OperationsはPromiseに標準対応しているので、軽率にasync/awaitをお使いいただけます。View最後にこれらをViewへ繋ぎます。useFleurContextを利用します。useStoreには第1引数に監視対象のStoreクラスを、第2引数にStoreから状態を取り出す関数を渡すことで、Storeの更新があった時にコンポーネントの更新が走ります。}を返すHooksです。ユーザーのアクションによって何かしらのoperationを実行する際にはexecuteOperationに実行したいoperationとその引数を渡します。getStoreは「見た目には影響がないけど、コールバック内でStoreの状態が欲しい」というシーンで有効です。(基本的にOperations内で済むはずです)最後に、アプリケーションの立ち上げ部分を書けば一通りのフローは完成です!質問Routerは?Routerのサンプルは@fleur/route-store-domのREADMEをご覧ください。SSRのサンプルは?この記事中では割愛しますが、こちらのサンプルをご覧ください。大規模な環境で動作させた時にメモリリークが発生しないか?という点のテストが不足しているので、フィードバックを頂けると幸いです。ベンチマークを取った感じこのようなスコアになっています。パフォーマンスを最適化した結果、Fluxibleより圧倒的に早く、react-reduxに対して肉薄するか追い越すほどのパフォーマンスになりました。Fleurが内部で行っている最適化は主に以下の3つです。SuspenseやGraphQLまわりの知見が少ないので改善点はまだあると思いますが、大体のチョットフクザツな案件には対応できる出来になっていると思います。最後に、ピクシブ株式会社ではアイスブレイクと称してドロップキックな話をしがちな強いフロントエンドエンジニアをいつでも募集しています。ragg2016年4月に新卒入社。フロントエンドエンジニアなので、悪い顔の顔が良い二次元の女の子が好き。z-indexを含む話はあんまり好きではない。Reserved.スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。