Chapter 06

1章.5 よりよいメンタルモデル形成:特徴を知る2

Satoshi Takeda
Satoshi Takeda
2021.10.01に更新

もう少しだけ React, Angular について触れていきます。

データフロー, State, UI

React や Angular の中で何と呼ぶかはさておき、State が UI を決定づけるというシンプルな考え方は昨今の宣言的 UI といった言い回しで、Web に限らず Flutter や SwiftUI, Jetpack Compose のネイティブアプリの文脈でもよく話されることでしょう。

UI = F(STATE)

上記のような抽象的な式をドキュメントやスライドで見かけたことがある人は多いはずです(もしかしたら View = Component(State) かもしれませんね)。

こういった単純な式を見てしまうと UI を決定づける State の取り回し方、つまりデータフローを制御できればバグの少ない、すばらしい UI が実装できそうな気がしてきます。

React の強いイデオロギー

React はデータフローが単方向であること、下方向にのみ流れることを明記しています

“単一方向” データフローと呼ばれます。
いかなる state も必ず特定のコンポーネントが所有し、state から生ずる全てのデータまたは UI は、ツリーでそれらの “下” にいるコンポーネントにのみ影響します

親のコンポーネントから子のコンポーネントへデータが流れることはあっても、子から親へデータが流れることはおおよそありません(例外もあるのでこのように言いますが詳細は Angular の項で触れます)。

なお Hooks が登場してからライフサイクルはすべて副作用 useEffect で処理されることがほとんどで、上流から流れてくる State, Props により冪等性のある DOM の結果を返すという React の主張が強くなりました。

https://twitter.com/sebmarkbage/status/1161459117342334976

イデオロギーといってもよい抽象化により多くの DOM 操作を React に委ねていることも少し心にとどめておいてもよいかもしれません。

下記はスライドに出てくるコードです。input.value を State として UI は再描画されます。以下の画面を操作するとわかりますが、input へのカーソルフォーカスが解除されますね。このことを考えてみると React が何を抽象化しているのか、Render should be pure の哲学の中にどういった DOM 処理を丸めているか理解の一助になりそうです。

子からイベントを受け取れる Angular

React は単方向データフロー(データバインディング)を中心とした考え方、そして state, props という 2 種類のデータを取り回して子に流していくスタイルでした。JSX により流れた値をそのまま JavaScript のコンテキストで JSX にバインドします。

Angular はデータをテキストやプロパティ、属性などにバインドするためのテンプレート構文を用意しています。my text is {{ value }}.[attr.aria-label]="value"[src]="imageUrl" などがテンプレートによく出てくるデータとバインドされた箇所になります。一方でイベントバインディングと呼ばれる構文 (click)="handler(value)" もあります。四角括弧 [], 丸括弧 () がテンプレートで表現されるのが特徴的です。

さらにコンポーネントには Input と呼ばれるデータの受け取り口だけではなく、Output と呼ばれる子から親へデータを渡すための逆の口がデフォルトで用意されています。子コンポーネントで取り回していたデータをそのまま親で受け取ることが可能です。

State に関心のある親コンポーネントと表示・UI イベントに関わる子コンポーネントの責務を分離したい、Container/Presentational なコンポーネント構成のケースにおいて、親にデータを戻したいシーンで容易に実現できます。

こういった State の繰り上げは Jetpack Compose でも状態の巻き上げのような名前で実装されています。React は State リフトアップと呼んでいます。子に関数を渡して遅延実行させるやり方ですね。Angular では Output があるので Input に関数を渡して子から捲り上げるということはしません。

Angular ではコンポーネントで Output をイベントバインディングの記法である (eventName) で表現します(命名もイベントと類推できる命名を推奨しています)。React は State リフトアップのために on*** のような接頭辞のイベントハンドラを想起する命名で props を渡します(「on の接頭辞は慣例的にイベントハンドラである」という暗黙知も含まれますね)。

これらからもわかるように、子コンポーネントから伝達されるべきはイベントであると解釈できるのではないでしょうか。

とらえておくべきメンタルモデルのひとつとして、UI は単方向データフローで State(状態)を子へ流す・イベントは末端から上流に送出される、といった方針が現代的な UI コンポーネントのあり方とも言えるでしょう。