Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
scalajs-reactで作るフロントエンド / Frontend With scalajs...
Search
yuiwai
September 30, 2019
Programming
610
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
scalajs-reactで作るフロントエンド / Frontend With scalajs-react
yuiwai
September 30, 2019
More Decks by yuiwai
See All by yuiwai
Lagomに学ぶマイクロサービス / Learn Microservice with Lagom
yuiwai
0
880
Scalaで作るカジュアルゲーム
yuiwai
1
580
Other Decks in Programming
See All in Programming
為什麼你並不需要ViewModel / No, you don't need a ViewModel
lovee
1
500
2年かけて Deno に DOMMatrix を実装した話 / How I implemented DOMMatrix in Deno over two years
petamoriken
0
210
SlackアプリとLambdaの 連携を構築した話
pawn_4_s
1
120
Detecting Compromised CI with eBPF and Cilium Tetragon
lizrice
0
180
ソフトウェアエンジニアにとっての生成AI - 特性を知って使い倒す / generative ai for software enginner
kishida
5
1.4k
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
360
AI時代のPHPer生存戦略 ~「言語、もうなんでもよくない?」に本気で向き合う~
vivion
0
490
Cloudflare is Agents
chimame
0
170
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
180
仕様書を書く前にハーネスを作る - Agent Native開発は「探索を速く、判定を固く」
gotalab555
4
1.6k
Japan Community Day at Kubecon + CloudNativeCon Japan 2026: Learning Container Privilege Control by Building My Own Low-Level Container Runtime
ternbusty
1
150
AIが無かった頃の素敵な出会いの話
codmoninc
1
430
Featured
See All Featured
Unsuck your backbone
ammeep
672
58k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
4.3k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
960
Statistics for Hackers
jakevdp
799
230k
Facilitating Awesome Meetings
lara
57
7.1k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
610
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
460
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
66
57k
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
280
A designer walks into a library…
pauljervisheath
211
24k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Transcript
scalajs-reactで作る フロントエンド Yuichiro.iwai 2019/09/30 - Fun Fun Functional (3)
自己紹介 岩井 雄一郎(SUGAR株式会社) Scalaをこよなく愛する37歳、道産子、2児の父 長らくソシャゲ業界でサーバサイドの開発をしてました。 4月からSUGARで、主にAndroidの開発をしています。 趣味はダーツ、ギター、将棋
今日のお話 このたび、フロントエンド(管理画面的なもの)を書くかも?ということになりまし て、かねてより愛用していたScala.jsで、Reactを利用するアプローチについて調 査しましたので、その概念をご紹介したいと思います。 (個人的に、もっとScala.jsが広まればいいな、と願っており、その宣伝も兼ねて おりますこと、ご了承ください)
Scala.jsとは? • Scalaで書いてJSにコンパイルされる、いわゆるAltJSの一種 • Scalaで書いたコード+一部のJavaライブラリ*が動く • Scala.js用にビルドされたScalaライブラリが利用可能 https://github.com/scala-js/scala-js *Scalaの動作の為に必要な Java依存部分を、Scala.js用に書いたもの(ライセンス的な)
Reactとは? • Facebook社が開発したJS向けのUIライブラリ • 状態とViewを分離し、仮想DOMを用いて効率的な描画を実現 • コンポーネントベース ◦ 再利用可能で副作用の無い小さな単位に切り分ける https://github.com/facebook/react/
Reactとは? State VDOM HTML ある状態の入力に対して、 VDOMが一意に決まり、結果として HTMLがレンダリングされる
Reactとは? State VDOM HTML modified! 状態の変化が通知されると、 VDOMが更新されて
Reactとは? State VDOM HTML modified! diff 以前のVDOMとの変更差分が算出され、それが HTMLへと反映される
scalajs-reactとは? • Scala.js用のReactライブラリ • Scalaで書きやすいような工夫がなされている • 副作用を局所化、型安全、Functionalなアプローチ • Routerなどの拡張も含んでいる https://github.com/japgolly/scalajs-react
scalajs-reactで扱う 主要な概念を見ていきます
State • Immutableなデータ構造 ◦ 更新時は、新しいStateを作り直す ◦ case classとの相性が非常に良い • Component内の動的な性質を保持する
◦ ユーザの操作に応じて変化する値 ◦ API通信など外部から取得した値 ◦ 時間経過で随時変化する値 ...など • 基本的には最上位の Componentにのみ持ちたい(次ページへ) case class State(wallet: Wallet) { def charge(amount: Point): State = copy(wallet = wallet.charge(amount)) } case class Wallet(balance: Point) { def charge(amount: Point): Wallet = copy(balance = balance.add(amount)) } case class Point(value: Long) extends AnyVal { def add(that: Point): Point = ... }
Root以外のComponentがStateを持つべきか? • 基本的には上位Componentから受け取ったPropsにのみ依存し、結果を決めたい ◦ データの流れを単方向にし、副作用を排除することで、再利用しやすくなる ◦ 下位Componentで変化したStateを上位に同期する流れは、構造が複雑化する • ただし、そのComponent内で完結し、揮発してしまっても構わない Stateなら、各Componentで
持った方が素直に書けると考えています ◦ 例えば、編集中のフォームのデータ、その画面内だけで有効な設定など
Props • Immutableなデータ構造 ◦ やはりcase classとの相性が良い • Componentの初期化時に渡される • Componentの静的な性質を保持する
◦ 上流にイベントを伝搬するハンドラ ◦ Globalな設定など ◦ 親Componentから受け継いだ属性 ...など case class Props( wallet: Wallet, // 状態の参照 settings: GlobalSetting, // 何らかの設定など handler: Action => Callback // イベントハンドラ ) // VDOMの中で使う例 (p: Props) <.div( s“残高: ${p.wallet.balance.value}”, <.button( ^.onClick --> p.handler(Charge), “チャージ” ) )
Component • singleton objectとして定義を格納する • StateとPropsを定義 ◦ StateはComponent内で可変 ◦ PropsはComponent内で不変
• Backend(後述)を定義 object WalletComponent { case class Props(...) case class State(...) class Backend(...) { ... } } 定義 Component 初期化 Props 実体
Component Backend • Component定義内にBackendというクラスを作成 ◦ このクラス内の処理が Componentの実体となる • renderメソッドを定義 ◦
State/Propsを受け取りVDOMを返す Props State Backend render() VDOM 状態の入力から、描画への出力を行う、いわば橋 渡し的な存在で、UIライブラリとしての中核を担う。
BackendScope • Backendがコンストラクタに受け取る ◦ このインスタンスを介して Stateを 操作したり、Propsを参照したり • Props/Stateを型パラメータに持つ ◦
BackendScope[Props, State] • renderメソッドの外からState/Propsに アクセスする場合、このインスタンス を経由し、Callbackとして扱う ◦ Callbackが評価されるタイミングで その時点のState/Propsを参照する // bsはBackendScopeのインスタンス bs: BackendScope[Props, State] // 現在のStateを受け取り新しいStateに更新 // modState(f: State => State): Callback bs.modState( s => ...) // bs.propsはPropsの参照を表す // CallbackTo[Props] // // このmapは // map[A](f: Props => A): CallbackTo[A] bs.props.map { p => ... }
階層化されたComponent • Componentは子として別なComponentを 持つことができる • 親から子へは、Propsを通じてデータを 受けわたす • 親のState(もしくはProps)が変化することで 子に変更が伝搬される
Parent Child Props Props State
VDOM • 属性値を持った要素のツリー構造 ◦ 実質、HTMLのタグに相当 • ユーザのインタラクションを イベント(後述)として受け取る // <.xxx(...)
で要素(タグ)を定義 <.div( // ^.xxx := ... で属性を定義 ^.id := “someId”, <.button( // --> イベントハンドラ ^.onClick --> { … }, // 文字列はそのまま子要素扱い “Click Me!” ), // Componentを子要素にする OtherComponent.Props(...).render )
Event(Handler) • クリックやキー入力などユーザ操作で発生 • EventをトリガーにCallback(後述)を返す ハンドラを実装することでリアクション ◦ ハンドラはEventオブジェクトを受け取る こともできる //
以下はVDOMの属性定義の文脈の前提 // -->[A](cb: => CallbackTo[A]): TagMod ^.onClick --> { ... } // ==> はイベントオブジェクトを受け取る // ==>[A](f: Event => CallbackTo[A]): TagMod ^.onKeyDown ==> { e => // ここでpersist()を呼んでおかないと // このCallbackが評価される時点で // 正しいイベントオブジェクトにアクセス // できない場合がある e.persist() … } ※TagModはVDOM中の構成要素として展開可能な型 イベントオブジェクトを受け取るケースでは、Callbackが 遅延評価されることと、イベントオブジェクトが再利用さ れることの兼ね合いで、persistを呼んでおかないと正常 に動作しないという罠が
Callback,CallbackTo[T] • 副作用を包み込む ◦ Stateの書き換え ◦ 外部通信などの非同期処理 ◦ ログ出力やAlertの表示 •
CallbackTo[T]はT型の結果を包んだもの • CallbackはCallbackTo[Unit]へのalias • >>や>>=などで合成できる • 非同期版のAsyncCallback[A]もある val cba: CallbackTo[A] = … val cbb: CallbackTo[B] = … val a2cbb: A => CallbackT[B] = … val asyncCbc: AsyncCallbackTo[C] = ... // シーケンシャルに実行 // >>[B](runNext: CallbackTo[B]): CallbackTo[B] cba >> cbb // >>= はflatMapのalias // >>=[B](f: A => CallbackTo[B]): CallbackTo[B] cba >>= a2cbb // AsyncCallback同士で処理する必要がある // callback.asyncでAyncCallbackに変換 cba.async >> asyncCbc
Component 全体像 React.js scalajs-react HTML VDOM User Backend State Event
Props Initialize View Action render() EventHandler Callback Modify Event
まとめ • scalajs-reactは、Scalaを使ってFunctionalなアプローチでWebフロントエ ンドを書けるソリューションです。 • Scala好き、あるいはScalaに興味があり、フロントエンドもScalaで書いてみ たいという方にはオススメです。 • Scala.jsはWebフロント以外のユースケースもあります! 興味を持っていただけたら、幸いです。
ご清聴ありがとうございました