読者です 読者をやめる 読者になる 読者になる

タオルケット体操

サツバツいんたーねっと

「Reactの難しさ」を分解しよう

ずっと感じてたもやもやを書き連ねたら長くなってしまったが、ぼんやりとReactとかなんか難しそうだしめんどくさいから新規案件だけどjQueryでやろっかなどうしよっかなーと迷っている人の指針になってくれればうれしい。

他のライブラリと比べるまえに

まず、ReactとjQueryと比べるのはやめよう

出た時から延々と言われ続けているものの、やっぱり今でもjQueryとReactを「比較」するようなことは行われていたりする。
これらは「DOMを操作する便利API集」と「GUIを構築するためのViewライブラリ」で、両者は戦場が同じだから下レイヤの操作はブラウザのDOMを叩いているというだけで、そもそも思想や目指している部分は全く違う。

そして今やWebアプリというものは、本格的なGUIアプリケーションを作る場所へとなりつつ(あるいはすでに)ある。
そういう世界観で、ブラウザAPIが簡単に叩けるだとか、タイプ数が少なくて済むという話があまり本質的でないのはお分かりいただけるだろう。各ブラウザベンダーが少しづつ足並みを揃えて標準化が進み、リッチなユーザ体験を提供するという段になって必要になるのは思想でありアーキテクチャであり、それを落とし込んだフレームワークだ。Win32 APIとMMVMのどちらが優れているかという議論をする人はいないだろう。それと同じである。

GUIアプリケーションを構築する際のハードルについては色々と存在するが、まずStateとViewの関係性を健全に保つことの重要性は皆同意するところだとおもう。MVCがやりたいことも突き詰めればそれだ。
ReactはViewがもつState*1とそのPresentation(ブラウザであればDOM)の流れを管理するというのがその目的で、addEventListenerは長いから短くしましょうとか、DOMの操作をメソッドチェインでクールにキメるぜというのはその思想の外側に存在する。

「テンプレートエンジン」として捉えて、シンタックスを攻撃するのをやめよう

後述するが、つまるところReactの目的は状態の管理と、そこからレンダリングされるViewが常に再現可能なことにある。ReactがシンプルにしたいのはStateをどうレンダリングするかの流れで、テンプレートのシンタックスなんかはその本質から少しはずれる。
なのでJSXは、単純にJavaScript側でオブジェクトのツリーを人間にやさしい形で構築できるようにするためのDSLで、それは目的でも手段でもない。

一時期、Riot.jsとかが炎上マーケティングっぽいものをしかけてきててもイマイチ盛り上がらなかったのも恐らくそこが原因で、当時Reactにうおーすげーってなっていた人が見ていたのは関数型的な思想をWebのViewの世界へ良い感じに持ち込んできたところで、そこに「HTMLの中に書けるシンタックスでコードが短い!」というぶつけ方はまるで見当違いだったのだ。もちろんイケてるシンタックスでコードが短いのは大事なことだけど、その下敷きになっている思想がセクシーであることが重要になる。

ライブラリとしてのReactはとても簡単

誰かが「Reactが難しい」という場合、それは概ね別のものを指していることが多い。
ライブラリとしてただ使うだけのReactは本当に簡単だ。ほんのちょっとのAPIしか存在していない。

その目的も、親から受け継がれて変更不可能(してはいけない)なpropsとみんな大好きstateという二つの属性に対して、出力したいViewをObject Treeで宣言しておく、というそれだけだ。それに付随してイベントサイクルのためのAPIがいくつか存在しているが、普通にプログラマーとしての勘が働く人ならばドキュメントを読むまでもなく存在を予想できる程度のもので、そこに複雑性はない(実際ドキュメントを読むとAPIの解説はあっという間に終了して、ほとんどが便利な書き方や思想の解説に終始している)。

もっと簡潔にまとめると、「状態に対して出力されるものが一意に決まるViewフレームワーク」というのがReactということになる。
Reactの思想にしたがってコードを書いていると、自然とViewの冪等性が担保されることになる。つまり同じStateからは常に同じViewが吐かれるということだ。Reactは可能な限りstateではなくpropsを使うように推奨しているのもそのためだ。

冪等性が担保されると何が嬉しいかについてはもはや語るまでもないとおもう。
例えば不具合の報告があったとして、ユーザと同じデータを揃えれば(理論上は)常に同じ状況が再現できることになる。例えば何も考えずにゴリゴリとDOMをいじくって作っているようなコードであれば、同じデータを揃えた後に特定のボタンを二回押して3回回ってワンと鳴かなければ再現しない、というものが生まれやすくなる。

VirtualDOMについては、View出力の冪等性を担保しつつパフォーマンスを維持できるアルゴリズムというだけで、そこはReactの思想の本質ではないため、二の次で良いとおもう。が、ある程度使い込むならば挙動なんかを理解しておかないと解決できない問題が生じることはままあるので頑張ろうな。

Reactの思想を理解するのはチョットムズカシイ

「状態に対して出力されるものが一意に決まる」といったけど、それはもちろんちゃんと思想にしたがってコードを書いた場合だけだ。Reactは関数型の思想、とりわけimmutabilityを大事にしているフレームワークで、そこについては利用者のリテラシーを信頼することで成り立っている。
なので例えば、propsを平気で書き換えたり、globalやprototypeを汚染するような書き方が癖になっているプログラマーが使えば全てが台無しになり、下手をすればjQueryだけでDOM操作をしていたときよりもわかりにくいエラーがでまくるという事態は十分起こりうる。

日本ではReactはWebアプリを書く際の第一選択肢といってもいいくらいに流行っているフレームワークだと言えるくらいになっている(雰囲気)だが、正直なところ僕はどれだけの人がReactの思想に共感して使っているのか、ということについて疑問におもっているし、Reactは思想抜きでなんとなく使って幸せになるライブラリではない。実際、「Reactを使ってもコードが短くならない」という声は未だに結構聞く。コードの量が問題になるようなところにReactはそぐわない(僕個人は小規模でも十分なメリットがあるとおもうけど)。

そういった部分はReactの難しさだとおもう。

環境構築が難しい

React自体はES.nextのシンタックスもJSXも要求していないのだが、現代の空気感としてReact書くなら両者は必須だよねという空気があることは否定できない。また僕自身も、まともなフロントエンドはそこらへんの環境整備なしに行えないという立場だ。
とはいえ、それが簡単だとは言わない。というか難しい。

ビルドツールはWebpackに定まりつつあるものの、ベストプラクティスがあるとは言い難い状況だし、そもそもWebpackは難しいしすぐに壊れる。Nodeのバージョンは頻繁にあがるし、バージョンをあげるたびに何かしら(主にsassのバインディングとか)が壊れる。

おそらく、Reactで挫折する人の9割はReactを書き始める前に、babelを含めた環境構築で挫折しているとおもう。
が、これは別にReact特有の問題ではない。

JavaScriptそのものが難しい

片手間でJavaScriptを書く時代じゃないという発言が出てからそろそろ3年目で、僕も気がついたらJavaScriptを書き始めて3年になってる。といいつつRubyやPythonのサーバーサイドもかなりの量を書いていたので結局片手間じゃねえかという気もしなくはないが、気持ち的には本職だ。

僕はプログラミング言語そのものが好きなので、例えばみんなが嫌うObjective-Cだってオブジェクト指向を実装に落とし込むというレベルでは美しくて良い言語だと感じている。でもJavaScript(ECMAScript)は本当に好きになれないし、学習がとても難しい言語だとおもう。

1995 – ブレンダン・アイクがプログラミング言語設計においてかつて犯されたあらゆる誤りを取り上げ、さらにいくつかの誤りを新たに考案し、LiveScriptを作る。後にこの言語はJavaの人気にあやかるためJavaScriptと改名された。さらにこの言語は皮膚病の人気にあやかるためECMAScriptと改名された。 不完全にしておよそ正しくないプログラミング言語小史

というジョークが存在しているが、おもわずみんなが納得してしまうくらいの説得力がある。
ES.nextでよくなったかというと、僕は全くそうはおもわない。

GUIが難しい

jQuery時代からのパラダイムシフト

率直に言って、(そもそも比較の対象にするのがおかしいというのは前述したとおりだが)jQueryのほうが楽だったという人たちは、「DOMを良い感じにアレしてインタラクションを作る」時代から「フロントエンドでGUIをやっていく」ことへのパラダイムシフトに失敗している。
そういう意味で、例えばWindowsやモバイルでGUIアプリケーションを作っていたWeb未経験の人間のほうが、長くJavaScriptを書いていた人よりもモダンなフロントエンド環境に早く適合するだろう。

逆に猫も杓子もReactやVueやAngularなせいで、「DOMを良い感じにアレしてインタラクションを作る」ので必要十分な要件にフレームワークを導入してSPAでレスポンシブデザインしてコストがかかりまくって死ぬみたいなことが各地で発生していそう。

僕からすると、「フロントエンドは複雑になった」わけではなく、「求められるものが複雑になった」「複雑なものに対応できるよう健全な成長を遂げた」だけで、ただし後者の成長の恩恵を受けるためには今までのブラウザでスクリプティングするという文脈から全く外れた、アーキテクチャレベルでの理解が必要になるというだけ。そして物事の考え方を変えるのは難しい。

フレームワーク関係者の情報量が多い

そもそもGUIは大変だし、理屈では割り切れない、人間の感性のためのチューニングを要求されたりして心が死んだりする。Fluxは割とシンプルな概念なんだけども、そもそもObserverがよくわかっていない人が多数派なんじゃないか疑惑はある。
また、ブラウザ特有の、本質的なデータストアはHTTP越しのサーバサイドにあるよ問題と、そこから生じるModelのデータ検証どうすんの問題についてfluxは知らん顔を決め込んでいる。Reduxに至っては非同期をちゃんとサポートしてくれていないせいでむしろ余計な混乱が生じている。JavaScriptと切っても切り離せない非同期の問題をmiddlewareに全部押し付けた上で純粋関数とかカマトトぶってるReduxちゃんマジうざくない?

RESTがもはや時代にそぐわないものになっているのは明らかなんだけど、GraphQLはまだ人柱すぎるし、リアルタイムWebとかどーなっちゃうのといいつつセキュリティもちゃんとしましょうね。などと、ここら辺になるとブラウザを離れてサーバーサイドスタックにも手を伸ばすような形になってくる。

……などの情報は事前に揃える必要はなくて、地雷をたくさん踏み抜いて複雑なアプリケーションを作っていく人たちが必要としている知識なんだけど、それまでの環境構築やフレームワーク、ReactiveStreamなんかのパラダイムシフトで脳がオーバーフローした結果、必要以上にエッジ(そしてunstable)な知識まで学ぼうとして心が折れてしまう。

SPAはとりわけ難しい

たまにある勘違いとして、ReactやAngularなどのフレームワークを使う = SPAをやるというものがある。
もちろんこれらはSPAをやる場合はほとんど必須といってもいいようなライブラリなんだけども、別にこれらを使ったからといってSPAにしなければならない道理はない。

例えば弊社のアプリは普通にDjango側のルーティングで画面遷移しながら、各画面をReactで制御するような形でやっている*2

フロントエンドの難易度をあげる要因というのはいくつかあるんだけども、その中でも

  • jQueryを使う

  • SPAにする

    • (まともなhistory制御)
  • レスポンシブにする

のこれら三つは、要素が重なると難易度が掛け算的に跳ね上がる。だが、この三つに関しては、採用をとりやめるという選択肢がとれる。

かなり知見がたまり、ツールが充実している現代でもSPAのコーディングはかなりめんどくさい。いやならやめてもいいんじゃよ?

SPAのメリットは、遷移に伴うフルレンダリングが引き起こすUX低下や、HTTPリクエストの増加を抑えたりとかイロイロ。コストがペイできる見通しや、実装できる実力のあるフロントエンジニアが揃っていない場合はSPAにしないという選択肢は全然ありだとおもう。

まとめ

ずいぶん長ったらしくなってしまったのでここでまとめる。

  • jQueryとReactと比較するのはそもそもナンセンスだからやめような

  • ReactのAPI自体はめちゃくちゃシンプルだし簡単

    • でもちゃんとドキュメントを読んで、思想を理解しないと恩恵を受け取れない可能性はある

    • Reactを使う = babelなどのトランスパイラやビルドツール周りが必須になる(でもここら辺使えない人がこの先生きのこるのはつらそう)

  • JavaScript自体が難しい問題

  • GUIが難しい

    • MVC(flux)は前提知識

    • jQuery時代からのパラダイムシフトができない層

    • 情報量がクソ多い

      • 界隈が盛り上がっている証拠でもあるので良いことではある
    • SPAになると難易度が跳ね上がる

      • 本当にSPA必要なの?

      • そもそもSPAで作れるスキルのエンジニアが在籍してるかどうか

恐らく「React難しい」って言ってる人で、本当にReactが難しい人は英語が視界にはいると蕁麻疹が出てしまって読めない人をのぞけば別の部分につまづいているはずで、そこを曖昧にしたまま別のフレームワークを選択したところで結局同じ問題にぶち当たってしまい幸せにはなれないはず。
逆にアーキテクチャや思想の部分は理解しておくとつぶしがきくので、例えばReactを使わない選択をしたり、別のフレームワークが流行っても幸せになれる。

はず。

*1:誤解を招きそうな表現だが、Storeで管理されているStateとComponent特有のStateというのは管理されてしかるべきで、ReactがStateを持つのもそういった理由による

*2:現在、SPAへの移行計画が進んでいるが