コンポジション vs 継承
今回は、コンポジション vs 継承を学びます。
子要素の出力
Sidebar
や Dialog
のような汎用的なコンテナコンポーネントでよく使われる方法。
children
を使い、受け取った子要素を出力。
function FancyBorder(props) { return <div className={'FancyBorder FancyBorder-' + props. color}>{props.children}</div>; }
他のコンポーネントから JSX をネストすることで任意の子要素を渡すことができる。
function WelcomeDialog() { return ( <FancyBorder color="blue"> <h1 className="Dialog-title">Welcome</h1> <p className="Dialog-message">Thank you for visiting our spacecraft!</p> </FancyBorder> ); }
スタイリングを分離します。
.FancyBorder { padding: 10px 10px; border: 10px solid; } .FancyBorder-blue { border-color: blue; } .Dialog-title { margin: 0; font-family: sans-serif; } .Dialog-message { font-size: larger; }
複数の箇所に子要素を追加するケース。 独自の props
を渡すことができる。一般的ではない。
<link rel="stylesheet" href="style.css" /> </head> <body> <div id="root"></div> <script type="text/babel"> function Contacts() { return <div className="Contacts" />; } function Chat() { return <div className="Chat" />; } function SplitPane(props) { return ( <div className="SplitPane"> <div className="SplitPane-left">{props.left}</div> <div className="SplitPane-right">{props.right}</div> </div> ); } function App() { return <SplitPane left={<Contacts />} right={<Chat />} />; } ReactDOM.render(<App />, document.getElementById('root')); </script> </body>
left={<Contacts />}
や right={<Chat />}
が独自の props
。コンポーネントに props
として渡せるものに制限はない。
スタイリングを分離します。
html, body, #root { width: 100%; height: 100%; } .SplitPane { width: 100%; height: 100%; } .SplitPane-left { float: left; width: 30%; height: 100%; } .SplitPane-right { float: left; width: 70%; height: 100%; } .Contacts { width: 100%; height: 100%; background: lightblue; } .Chat { width: 100%; height: 100%; background: pink; }
行く通りかの書き方ができるCSS。それぞれのメリット・デメリットがわかっていない。
コンポジションとは何かがわかりましたね。ページの各々の構成要素をどのように組み立てるのか。
React を使った組み立ての方法論と捉えればよさそうです。
特化したコンポーネント
WelcomeDialog
は Dialog
の特別なケースと捉える。これはコンポジションで実現できる。
function FancyBorder(props) { return <div className={'FancyBorder FancyBorder-' + props.color}>{props.children}</div>; } function Dialog(props) { return ( <FancyBorder color="blue"> <h1 className="Dialog-title">{props.title}</h1> <p className="Dialog-message">{props.message}</p> </FancyBorder> ); } function WelcomeDialog() { return <Dialog title="Welcome" message="Thank you for visiting our spacecraft!" />; } ReactDOM.render(<WelcomeDialog />, document.getElementById('root'));
継承はどうなの?
Reactの流儀
MAIN CONCEPTS 最後の章 [Reactの流儀[(https://ja.reactjs.org/docs/thinking-in-react.html) です。
さしずめ「ハンズオン React アプリ」といった内容です。
Step1
Step2
表示の実装を行った後に、ユーザ操作の実装を行うのがよい。 UI の描画だけを行い、ユーザの操作をできない静的なバージョンを作ることからスタートする。
静的なバージョンを作る際には、
Step3
最初に更新可能な状態の最小構成を考える。データモデルの更新は state
で実現できる。
必要なものが出てきたら、その都度実装する。
state
として扱うもの
- 時間の経過の中で変化する
- 算出することもできない
Step4
どのコンポーネントがどんな state を持つべきなのかが、最も難しい問題。
state
を使って表示を行う、すべてのコンポーネントを確認する- 共通の親コンポーネントか、さらに上位の別のコンポーネントが state を持っているべきである
state を持つにふさわしいコンポーネントがない場合は、
state` を保持するためだけの新しいコンポーネントを作り、これを共通の親コンポーネントに配置する。
filterText
に ball
を設定し、フィルター動作を確認する。
this.state = { filterText: 'ball', inStockOnly: false, };
Step5
SearchBar
コンポーネントから FilterableProductTable
コンポーネントを更新できるようにする。
ユーザがフォームを更新するたびに、入力を反映し state
を更新する。
state
自身で更新するばきなので、 FilterableProductTable
は SearchBar
にコールバックを渡す。このコールバックを onChange
イベントで発火させる。
完成したコードは公式サイトからリンクを辿ってください。
私は早々に挫折した。
コードリーディング
挫折したのでコードリーディングしてお茶を濁しておきます。
Step1 で行ったコンポーネント抽出に従いクラス形式でコンポーネントを宣言します。
class ProductCategoryRow extends React.Component { ... } class ProductRow extends React.Component { ... } class ProductTable extends React.Component { ... } class SearchBar extends React.Component { ... } class FilterableProductTable extends React.Component { ... }
ProductCategoryRow
コンポーネントはセルを横方向に結合。レンダーします。
JSON API
のデータ構造より child
は category
を割り当てます。
render() { const category = this.props.category; return ( <tr> <th colSpan="2"> {category} </th> </tr> ); }
ProductRow
コンポーネントは PRODUCTS
リストを1行で表示する。変数と name
を格納する変数が必要です。
name
は変数に格納するのに、 price
は変数に価格のしない理由は何? name
は stock
の値によりスタイリングするからだろうと思います。
const product = this.props.product; const name = product.stocked ? product.name : <span style={{color: 'red'}}> {product.name} </span>; return ( <tr> <td>{name}</td> <td>{product.price}</td> </tr> );
ProductTable
コンポーネントはインプットボックスやチェックボックスの状態により、表示をフィルタリングする。
私はここで挫折した。
if (product.category !== lastCategory) { rows.push( <ProductCategoryRow category={product.category} key={product.category} /> ); }
答えを見て厳密不等価演算子を厳密等価演算子に書き換えてどういう結果になるかは確認したが、理解はできない。
コーディングはここで終了とさせてください。
終わりに
終わりに として React のストロングポイントを抜粋する。
- モジュール化されていて明示的であるコードはより読みやすい
- コードを再利用が増えるに従い、書くコードの行数は減る
MAIN CONSETPS を通して学べたこと。
props
とstate
の違い- コンポーネントの組み立てと考え方
- イベント処理
- 要素のレンダー
- フォームの扱い
これらを読み解き理解できるようになった。しかし、ハンズオンで作成した規模のアプリをスムーズにコーディングできるわけではない。
チュートリアルに沿って学んだので、 create-react-app
でアプリを作る具体的な方法は不明なままだ。
さしずめ、素振りをするらめのバットの握り方を覚えた状態だろうか。
さらなる学習が必要です。