Reactはちょっとしたコツを掴むと一気に理解が進みます。
Googleのデベロッパーであり数々のReactトレーニングを手がけてきたTyler McGinnis氏によるReact "Aha" Momentsが非常に参考になるため、本人の許可を得て意訳しました。
誤りやより良い表現などがあればご指摘頂けると助かります。
原文:https://tylermcginnis.com/react-aha-moments/
私が技術的なコンテンツを教えたり書いたりする時の主な目標の1つは「アハ体験」を最大化することです。アハ体験は物事が突然理解できた瞬間のひらめきです。私たちは皆これを体験してきましたし、私の知る最高の教師たちは聴衆に応じて、それらの瞬間を最大化するための教えを最適化することができます。
ここ数年、私はほぼ全ての一般的なメディアでReactを教えてきました。その間、私はReact習得における「アハ体験」の引き金について書き留めてきました。2週間ほど前、私は偶然Redditのこのスレッドを見かけ、そこには同じアイデアが詰まっていました。そこで私がこの記事で意図しているのは、これらの体験についての知見を共有しつつ、Redditのスレッドで紹介された知見についても私の考えを述べることです。あなたがReactについてまだよく分かっていないようでしたら、この記事がその助けになることを願っています。
fn(d) = V。UIは状態の関数であり、コンポーネントにとってのpropsは、関数にとっての引数のようなものです。
React最大の利点の1つは、いつどこでReactコンポーネントを作成するかについて、JavaScriptの関数と同じ感覚をそのまま適用できることです。しかしながら、関数がいくつかの引数を受け取って値を返すのに対し、Reactはいくつかの引数を受け取ってUIのオブジェクト表現を返します。この考えは fn(d) = V という式でまとめられます。関数はデータを引数として受け取ってビューを返します。これはUI開発を考える上で最適な方法です。というのも、UIが異なる関数の呼び出しのみで構成されるからです。この手法は既に皆さんがアプリケーション開発で慣れ親しんでいるので、UI構築の際には関数合成の全ての利点を享受できるようになります。
Reactでは、アプリケーションのUIは関数合成によって構築され、JSXはそれらの関数を抽象化しています。
React初心者はほぼ例外なく「React自体は良さそうだけど、JSXがキモいんだよね。関心の分離ができてないし」と言います。JSXはHTMLになろうとしているわけではなく、間違いなくテンプレート言語以上のものです。JSXを理解するために重要な2つのポイントがあります。1つ目は、JSXはReact.createElementの抽象化であるということです。その関数はDOMのオブジェクト表現を返します。それは冗長なのですが、JSXを書くたびにトランスパイルされ、実際のDOM(もしくはiOSやAndroidなどのプラットフォームでのビュー)を表現するオブジェクトになります。さらにReactはそのオブジェクトを分析し、変更前後の差分を抽出して、変更のあったDOMのみを更新します。これにはいくつかのパフォーマンス上の問題がありますが、もっと重要な点はJSXが本当に「単なるJavaScript」であることです。2つめは、JSXが単なるJavaScriptであることで、JavaScriptの宣言性(そして馴染みやすさ)はもちろんのこと、合成、静的検証やデバッグなどのJavaScriptが提供する全ての利点を活かすことができます。
コンポーネントは必ずしもDOMノードに対応していなくても良いです。
Reactを学び始めると、「コンポーネントはReactの建築用ブロックである」と教わります。入力を受け取り、UIを返すということは、全てのコンポーネントが直接UIを返さなくてはならないということでしょうか?他のコンポーネントをレンダリングするコンポーネント(HOCパターン)が必要な場合はどうでしょうか?stateの一部を管理し、UIを返す代わりにstateを渡す関数呼び出しを返すコンポーネント(レンダーpropsパターン)は?ビジュアルUIではなくサウンドを管理するコンポーネントがあったらそれは何を返すのでしょうか?Reactの素晴らしい点は、コンポーネントから典型的な「ビュー」を返す必要はないということです。最終的に返されるのは、React要素、nullまたはfalseです。素敵でしょう?
他のコンポーネントだって返せます。
render() {
return <MyOtherComponent />
}
関数呼び出しも。
render() {
return this.props.children(this.someImportantState)
}
何も返さなくてもOKです。
render() {
return null
}
Ryan Florence氏のReact Rally talkはこの原則をより深くカバーしているのでオススメです。
2つのコンポーネントがstateを共有したい場合、stateを同期させるのではなくstateを持ち上げる必要があります。
コンポーネントベースのアーキテクチャでは、stateの共有は自ずと困難になります。2つのコンポーネントが同じstateに依存する場合、そのstateはどこにあるべきでしょうか?これは最終的にReduxで決着した解決策のエコシステム全体を活性化するような人気のある質問でした。Reduxの解決策は「store」と呼ばれる別の場所にstateを集約することです。コンポーネントはstoreを必要な部分だけ購読でき、storeを更新するための「actions」をdispatchすることもできます。Reactの解決策は2つのコンポーネントの最も近い親を探し、そこで共有stateを管理して必要に応じて子コンポーネントに渡すことです。どちらのアプローチにも一長一短がありますが、その選択肢を認識することが重要です。
Reactでは継承は不要であり、封じ込めと専門特化の両方を合成によって達成できます。
Reactは正当な理由により、関数型プログラミングの原則を採用するのに寛大です。React v0.13がES6クラスのミックスインをサポートをしないことが明らかになった時、継承から脱却して合成に向かって前進しました。その理由は、ミックスイン(または継承)によって達成できることのほとんど全てが合成によっても達成でき、しかも副作用をより抑えることができるからです。もしあなたが継承を重んじる考え方からReactに入ってきたのであれば、この考え方は難しく、おそらくあまり自然ではないと感じられることでしょう。幸運なことに、いくつかの素晴らしいリソースがあります。この動画はReact固有の内容ではないですが役立つでしょう。
コンテナコンポーネントとプレゼンテーショナルコンポーネントの分離
Reactコンポーネントの解剖学について考える時、state、潜在的なライフサイクルフック、そしてJSX経由のマークアップが通常含まれます。それら全てが1つのコンポーネントに含まれる代わりに、stateとライフサイクルフックをマークアップから分離したらどうでしょうか?これにより2つのコンポーネントが生まれます。最初の1つは、stateとライフサイクルメソッドを持ち、コンポーネントがどのように動作するのかについて担当します。2つ目は、propsとしてデータを受け取りコンポーネントの外観を担当します。このアプローチにより、プレゼンテーショナルコンポーネントは受け取ったデータに結合されなくなるため、再利用性が向上します。さらに、あなた(もしくはプロジェクトへの新規参入者)がアプリケーションの構造を理解しやすくなります。開発者はUIを意識することなくコンポーネントの実装を取り替えることができ、その逆に、デザイナーはプレゼンテーショナルコンポーネントがどのようにデータを受け取るかを心配することなくUIを微調整することもまた可能になります。
この話題についての詳細は、プレセンテーショナルとコンテナコンポーネントを参照してください。
ほとんどのコンポーネントをピュアにすれば、ステートレスなものは保守がずっと簡単になります。
これはプレゼンテーショナルコンポーネントをコンテナコンポーネントから分離するもう1つの利点です。stateは一貫性の欠如と切り離して考えることはできません。適切な境界線を引くことで、複雑さをカプセル化してアプリケーションの予測可能性を大幅に改善させることができます。