16 KiB
id | title | prev | next | redirect_from |
---|---|---|---|---|
thinking-in-react-ja-JP | Reactの考え方 | tutorial-ja-JP.html | conferences.html | blog/2013/11/05/thinking-in-react.html |
Pete Hunt著
ReactはJavascriptで大きなアプリを早く作成するのに良い方法だと私は思います。FacebookとInstagram製で規模を大きすすることも容易いです。
Reactのたくさんある中の偉大なパーツの1つのは、あなたに構築するアプリについて考えさせます。このページで、私はReactを使って検索可能な製品データテーブルを構築する思考プロセスをご案内します。
モックから始めましょう
私たちはすでにデザイナーからJSON APIとモックをもらっていると想像してください。このモックを見ればわかるように私たちのデザイナーは明らかに良くないです。
JSON APIはこれらのようなデータを返します。
[
{category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
{category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
{category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
{category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
{category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
{category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
];
ステップ1 : コンポーネント階層を用いてUIを改築します
まずあなたが行いたいことは、モックに必要なすべてのコンポーネント(サブコンポーネントも)の箱を作り、名前をつけることでしょう。もしあなたがデザイナーと共に働いているなら、すでに行っているかもしれませんが、彼らと話に行きましょう!彼らのPhotoshopのレイヤーの名前をReactのコンポーネントの名前にして終わらせることもできます。
独自コンポーネントがどのようにあるべきか知っていますか? あなたが新しい関数やオブジェクトを作る必要があるかどうかを決定するために同じ技術を使用しています。そのうちの一つのテクニックが単一責任の原則(single responsibility principle)です。これは一つのコンポーネントは一つの振る舞いをするべきというものです。もしこれが難しいのであれば、より小さなサブコンポーネントに分ける必要があります。
あなたは多くの場合、ユーザーにJSONデータ・モデルを表示しているので、あなたのモデルが正しく構築された場合、あなたのUI (したがって、コンポーネントの構造)がうまくマッピングされることがわかります。なぜなら、UIとデータモデルは同じ情報構造を持つ傾向にあり、つまりコンポーネントにUIを分離する作業は些細なことです。あなたの一つのデータモデルを表すコンポーネントに分割することが重要です。
これは私たちのシンプルなアプリの5つのコンポーネントです。各コンポーネントが表すデータが斜体になっています。
FilterableProductTable
(orange): 例のすべてを含んでいます。SearchBar
(blue): ユーザー入力のすべてを含んでいます。ProductTable
(green): displays and filters the ユーザー入力に基づいたデータコレクションを表示、選別しますProductCategoryRow
(turquoise): カテゴリーごとに表示します。ProductRow
(red): 製品の行ごとに表示します。
ProductTable
を見ると、テーブルのヘッダー("Name"と"Price"ラベルを含んでいる)が独自コンポーネントではないとわかります。これは好みの問題で、どちらの方法が良いかの議論があります。この例では、ProductTable
の責任の範囲であるデータ収集レンダリングの一部であるため、私はProductTable
の一部として残しました。しかし、もしこのヘッダーが複雑になりそうであれば、(ソートにアフォーダンスを追加した場合など)新しい ProductTableHeader
という独自コンポーネントを作る意味が高まります。
私たちは自分たちのモックのコンポーネントを特定し、階層に配置してみましょう。これらは簡単です。モックの中の他のコンポーネントと一緒に使用するコンポーネントはその子供の階層にいなくてはいけません。
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
ステップ2 : 静的なReactの構築
あなたは今コンポーネントの階層を持っており、アプリを実装する時がきました。最も簡単な方法はあなたの持っているデータモデルを受け取りUIをレンダリングするが、双方向性を持っていないバージョンを構築することです。静的なバージョンを構築するには多くのコードを書く必要があるが考えることはなく、双方向性を加えるためには熟考する必要がありコード量は多くないです。そのためこれらのプロセスは分離するのが最善の手段です。理由をみていきましょう。
データモデルをレンダリングさせる静的なバージョンを構築するために、あなたは他のコンポーネントを再利用したり、propsを使用してコンポーネントを構築したいと思うでしょう。propsとは親から子へデータを受け渡す方法のことです。もしあなたがstateのことを知っているなら、この静的なバージョンを構築する際には絶対にstateは使わないでください。stateは双方向性のためにのみ予約されます。それはつまり、時間の経過とともにデータが変化していきます。静的なバージョンのアプリを作っている間はこれは必要のないものです。
あなたはトップダウンでもボトムアップでも構築することができます。つまりあなたは階層の高い場所から(FilterableProductTable
)でも、低い場所から(ProductRow
)でもコンポーネントを構築し始めることができます。簡単な例ではトップダウンから行く方が簡単ですし、大規模なプロジェクトではボトムアップを行って、構築するテストを書く方が簡単です。
この最後のステップは、データモデルをレンダリングできる再利用可能なコンポーネントのライブラリーを手に入れることです。これはあなたのアプリの静的なバージョンのため、コンポーネントはrender()
メソッドのみを持ちます。階層のトップのコンポーネント(FilterableProductTable
)は部品としてデータモデルを持ちます。あなたが下の階層にあるデータモデルの変更を行い、ReactDOM.render()
を呼ぶと、UIも更新されます。複雑なことは何もないので、どのようにあなたのUIが更新され、変更した場所を確認するのは簡単です。Reactの1方向のデータフロー (1方向バインディングともよばれます。)はすべてのモジュール式と速さを保ちます。
このステップでヘルプが必要な場合はここに簡単な説明があります。React docs
コラム: props か state か?
Reactにはpropsとstateの2つのタイプの"model"データがあります。この二つの違いを理解することは重要です。違いを明確に理解していない場合はここを参照してください。the official React docs
ステップ3 : UI部品の最小限の(しかし完全な)表現を特定します
UIをインタラクティブに作るために、下の階層のデータモデルを変更を行えるようにする必要があります。Reactでは state を使うことで簡単に行うことができます。
アプリの構築を正しく行うために、まずそのアプリのニーズの可変状態の最小セットを考える必要があります。ここで重要なのはDRY、つまり繰り返さないこと、です。アプリケーションの状態の絶対的な最小表現を把握し、オンデマンドで必要他のすべてを計算します。たとえば、TODOリストを構築するとすると、TODOのリストを配列で保持し、個別にカウントの状態変数を保持しません。TODOの個数をレンダリングするときに、単にTODOのアイテムの長さを取得すればよいのです。
今回のアプリケーションのデータのすべてを考えます。私たちは以下を持っています。
- 独自製品のリスト
- ユーザーが入力した検索テキスト
- チェックボックスの値
- 製品のフィルターリスト
それぞれ一つを通し、それぞれの状態を把握しましょう。それぞれのデータに尋ねる質問は3つです。
- それはpropを経由して、親から渡されますか?そうなら、おそらくstateではありません。
- それは時間の経過とともに変化しますか?そうでないなら、おそらくstateではありません。
- あなたはそれがコンポーネント内の他のstateやpropに基づいて計算することができますか?そうなら、おそらくstateではありません。
その製品の独自のリストはpropとして渡されるので、stateではありません。検索テキストとチェックボックスは時間の経過とともに変化し、それを計算することはできないので、stateのように見えます。最後に、検索テキストとチェックボックスの値を持つ製品の独自のリストを組み合わせることによって計算することができるため、製品のフィルタリングされたリストはstateではありません。
つまり最終的にstateはこうなります。
- ユーザーが入力した検索テキスト
- チェックボックスの値
ステップ4 : あなたのstateがあるべき場所を特定します。
ここまでで、私たちはアプリの状態の最小セットが何であるかを認識することができました。次はコンポーネントの変化か、またはそれ自身の状態を特定する必要があります。
メモ:Reactはすべてのデータがコンポーネントの階層が1方向に流れるものです。コンポーネントがどのような状況にあるかはすぐには明確にはならないかもしれません。** これはしばしば新しく学ぼうとする方には難しいものになるため** 把握するために次の手順に従います。
アプリ内のそれぞれのstate
- 幾つかのstateに基づくレンダリングをするすべてのコンポーネントを識別する
- 共通の所有者を持つコンポーネント(階層内のstateを必要にするすべてのコンポーネント上にあるコンポーネント)
- 階層内の最大の、共通の所有者または別のコンポーネントのいずれか
- 意味のあるstateにコンポーネントを見つけることができない場合には、状態を保持するために、単純に新しいコンポーネントを作成し共通の所有者のコンポーネント上の階層のどこかに追加します。
アプリケーションのためにこの戦略に沿って実行しましょう
ProductTable
はstateに基づいた製品リストのフィルターを必要とし、SearchBar
は検索テキストとチェックの状態を表示する必要があります。- 共通のコンポーネントの所有者は
FilterableProductTable
です。 FilterableProductTable
にあるフィルタテキストと確認された値は概念的に理解する。
stateはFilterableProductTable
の中にあると決定しました。まずはアプリケーションの最初の状態を反映させるため、FilterableProductTable
に {filterText: '', inStockOnly: false}
を返す、getInitialState()
という関数を追加します。次に、ProductTable
と SearchBar
にpropとしてfilterText
とinStockOnly
を通します。最後にこれらのpropをProductTable
の行のフィルターに使い、SearchBar
のフォームのフィールドの値をセットします。
アプリケーションがどのように動作("ball"
にfilterText
をセットし、アプリを更新する)するかを見て開始することができます。データテーブルが正しく更新されていることがわかるでしょう。
ステップ5 : 逆のデータフローを追加
ここまで、階層の下へ向かってpropやstateの関数として正しくレンダリングするアプリを構築しました。ここからは逆の方向へのデータのフローをサポートします。深い階層のフォームのコンポーネントはFilterableProductTable
のstateを更新する必要があります。
Reactは、このデータの流れがどのようにあなたのプログラムが動くのかの理解を簡単にする流れですが、それが伝統的な双方向のデータバインディングよりも少しタイピングを必要とします。ReactはReactLink
という双方向バインディングを便利に実装できるアドオンを提供していますが、この投稿の目的は私たちが全てを明確にすることです。
例えば、あなたが入力するか現在のバージョンのチェックボックスをオンにしようとすると、あなたの入力をReactは無視することがわかります。これは意図的なもので、私たちは常にFilterableProductTable
から渡された state
に等しくなるように input
の値を設定しているためです。
何を起こしたいのかを考えましょう。私たちは確かにユーザーがどのようにフォームを変えても入力した値をstateに反映させようとしています。コンポーネントが自身のstateを更新する必要があるため、FilterableProductTable
はSearchBar
が更新するべき時にはいつでもコールバックを渡します。入力の通知に対してonChange
イベントを使用できます。そしてFilterableProductTable
によって渡されたコールバックはsetState()
を呼び出し、アプリが更新されます。
これは複雑に見えますが、それはわずか数行のコードのみです。そしてデータがどのようにアプリ内を流れているか、とても明白です。
以上です。
この記事を通してReactでコンポーネントとアプリケーションを構築するかをどのように考えるかのヒントになれば幸いです。あなたが今まで使ってきたものよりも少しタイピングが必要かもしれないが、そのコードは書かれているよりもはるかに多くを認識し、このモジュールを読むためにとても簡単でかつ明白なコードです。あなたが大きなコンポーネントのライブラリを構築するのなら、この明示性とモジュール性に感謝するでしょうし、コードの再利用性とコードのあなたのラインが縮小し始めます。 :)