# React 3. Reactの基礎2(学習日:10/26) ## 1. 条件付きレンダー レンダー(イベントで情報を渡す側)は、分岐や条件(if文等)に応じた振る舞いを定義できる。 ### 要素変数 state(変数の状態)により、異なる振る舞いを行うことができる。 ### インラインでの条件記述 ifを使わず、インラインによる条件記述方法もある。 * &&演算子を用いる true && expression(表現)だとexpressionが表示され、false && expressionだとfalseとなり何も表示されない。 この性質を利用して、expressionが表示されるかどうかを記述する。 ```javascript= let temperature = 30.0 let airConditioner = false //temperatureが28.0以上なら、airConditionerの動作をtrueにする。 {temperature >= 28.0 && airConditioner = true} ``` * 条件演算子 condition ? true: false を用いる ```javascript= let temperature = 30.0 let airConditioner = false //temperatureが28.0以上なら、airConditionerの動作をtrueにする。 airConditinoner = {temperature >= 28.0 ? true: false} ``` ### コンポーネントのレンダーを防ぐ 特定の条件の場合にレンダーしない選択肢を記述する場合のreturnはnullとなる。 ## 2. リストとkey ### mapを使った配列のレンダー Reactでは、配列を要素のリストに変換する。 map()を利用して要素を格納することで、それをレンダーできる。 ```javascript= const metro = ["midosuji", "tanimachi", "yotsubashi"] const listItems = metro.map((line) => <li>{line}<li>); ReactDOM.render( <ul>{listItems}</ul> document.getElementById('root') ); ``` ### コンポーネント内のレンダーとkey コンポーネント内でレンダーできるよう、propsの設定を行う。但し、リストには原則としてkeyを与えなければならない。 ```javascript= function Metro(props){ const metro = props.metro; const listItems = metro.map((line) => //keyを設定する(ここではindexとする) <li key={index}> {line} <li> ); return(<ul>{listItems}</ul>); } const metro = ["midosuji", "tanimachi", "yotsubashi"] ReactDOM.render( <Metro metro={metro} /> document.getElementById('root') ); ``` ### keyの特徴と条件 * Keyは、どの要素が追加や変更、削除されたかを識別するために役立つ。 * 原則としてそれぞれの項目に一意に特定できるような文字列を与える。多くはidをキーとする。インデックスも使えるが、追加、削除時に指し示す項目が変化してしまうため避ける。 * コンポーネントの抽出時は、keyは配列内の要素に残しておくべきである。具体的には、map()メソッド内の呼び出しの中に現れる要素にkeyが必要である。 * keyは兄弟要素の中で一意である必要があるが、全く異なる配列を作る場合のkeyは重複しても良い。 * 明示しない限りコンポーネントにkeyが渡されることは無い。 ## 3. フォーム ### フォームと制御されたコンポーネント Reactでは、HTMLのような変更されうる状態はstateプロパティに保持され、setState()関数でのみ更新される。 state上の情報を「信頼できる唯一の情報源」とすることで、React上でHTMLを制御することができる。 ### textareaタグ textarea要素は、テキストを子要素として定義する。 Reactではvalue属性を利用する。 ### selectタグ select要素で、ドロップダウンリストを作成する。 Reactではvalue属性を親のselectタグで使用する。 ### file inputタグ input type = "file"によって、ユーザにデバイス内の1つ以上のファイルを選ばせて、サーバへのアップロードやFile APIによるJavaScript操作が行える。この値は読み取り専用となるため、非制御コンポーネントである。 ### 複数の入力処理 入力要素にname属性を追加すれば、一意となるnameを指定して、event.target.nameで処理を先駆させることができる。 ### 制御された入力におけるnull値 制御されたコンポーネント上でvalueをプロパティに値に指定すると、ユーザが変更できなくなる。 ```javascript= class NameForm extends React.Component { constructor(props) { super(props); this.state = {value: ''}; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert('A name was submitted: ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> Name: <input type="text" value={this.state.value} onChange={this.handleChange} /> </label> <input type="submit" value="Submit" /> </form> ); } } ``` ## 4. Stateのリフトアップ 複数のコンポーネントが同一の変化をするデータを反映する必要性がある。 その場合は、最も近い共通の祖先へコンポーネントへ、共有されているstateをリフトアップする。 1. 2つ以上の入力フィールドを作り、それらを同期させておく場合、まずは両者を互いに変換できる関数を作成し、数値の扱いも定義する。 2. 2つの項目が参照するコンポーネントは、独立してローカルのstateを保持している。これらを同期させるには、stateを必要とするコンポーネント全てに対する、直近の共通祖先コンポーネントに移動させる。(下の例の場合、TempertureInputが持っていたローカルのstateを削除(参照先をpropsに)し、親コンポーネントのCalculatorに移す。(temperatureとscaleをローカルなstateに保存する))→リフトアップ ```javascript= const scaleNames = { c: 'Celsius', f: 'Fahrenheit' }; function toCelsius(fahrenheit) { return (fahrenheit - 32) * 5 / 9; } function toFahrenheit(celsius) { return (celsius * 9 / 5) + 32; } function tryConvert(temperature, convert) { const input = parseFloat(temperature); if (Number.isNaN(input)) { return ''; } const output = convert(input); const rounded = Math.round(output * 1000) / 1000; return rounded.toString(); } //親コンポーネント Calculator class Calculator extends React.Component { constructor(props) { super(props); this.handleCelsiusChange = this.handleCelsiusChange.bind(this); this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this); this.state = {temperature: '', scale: 'c'}; } handleCelsiusChange(temperature) { this.setState({scale: 'c', temperature}); } handleFahrenheitChange(temperature) { this.setState({scale: 'f', temperature}); } render() { const scale = this.state.scale; const temperature = this.state.temperature; const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature; const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature; return ( <div> <TemperatureInput scale="c" temperature={celsius} onTemperatureChange={this.handleCelsiusChange} /> <TemperatureInput scale="f" temperature={fahrenheit} onTemperatureChange={this.handleFahrenheitChange} /> <BoilingVerdict celsius={parseFloat(celsius)} /> </div> ); } } //子コンポーネント TempertureInput class TempertureInput extends React.Component{ constructor(props){ super(props); this.handleChange.bind(this); this.state = {temperature: ''}; } handleChange(e){ this.props.onTemperatureChange(e.terget.value); } render(){ const temperature = this.props.temperature; const scale = this.props.scale; return( <fieldSet> <legend>Enter temperture in{scaleNames[scale]}:</legend> <input value={tempreature} onChange={this.handleChange} /> </fieldset> ); } } ``` ## 5. コンポジションVS継承 ### 子要素の出力 コンポーネントの子要素が汎用的な入れ物を表すような場合、childrenというpropsを用いて子要素を渡すことができる。 ### 特化したコンポーネント Dialogの場合も、propsを渡して指定することで、より特化したコンポーネント作成が可能である。 ### コンポジションの特徴 クラスとして定義されたコンポーネントに対しても、上記の出力が可能である。 Reactでは、コンポジションによって十分な柔軟性が得られるため、継承による構造が推奨されることはない。 ## 6. Reactを用いたプログラム作成 ### 1. UIをコンポーネント階層構造に落とし込む。 できる限り1つのコンポーネントには1つのことだけをさせるべきである。(単一責任の原則)また、UIがデータモデルと同じ情報の構造を持つ傾向にあるため、コンポーネントが厳密に一部分だけを表現するように落とし込む。 ### 2. Reactで静的なバージョンを作成する。 データモデルを受け取ってUIだけの描画を行い、ユーザから操作できないようにする。stateは使わない。 ### 3. UI状態を表現する必要十分なstateを決定する。 インタラクティブなUIを表現するためにstateを策定する。 更新可能な最小構成を把握することが大切。(DRY:Don't Repeat Yourself) stateの数も最小限に。stateになりえるもの(propsや他のstateを経由して算出できず、時間経過で変化するもの)を探す。 ### 4. stateをどこに配置すべきかを明確にする。 以下の項目を確認する。 1. その state を使って表示を行う、すべてのコンポーネントを確認する。 2. 共通の親コンポーネントを見つける。(その階層構造の中で、ある state を必要としているすべてのコンポーネントの上位にある単一のコンポーネントのこと。) 3. 共通の親コンポーネントか、その階層構造でさらに上位の別のコンポーネントが state を持っているべきである。 4. もし state を持つにふさわしいコンポーネントを見つけられなかった場合は、state を保持するためだけの新しいコンポーネントを作り、階層構造の中ですでに見つけておいた共通の親コンポーネントの上に配置する。 ### 5. 逆方向のデータフローを追加する。 逆のデータフローも通るようにする。 ###### tags: `React`