要素のレンダー
要素のレンダー
要素とは React アプリケーションの最小単位の構成ブロックです。
要素は画面に表示したいものの説明書き。
<h1>Hello, world</h1>
React 要素はプレーンなオブジェクト、安価に作成できる。安価というのは、素早くハードの省資源で作れるという意味。
描画する
React だけで構築されたアプリケーションは、ルート DOM ノードは1つ。
既存アプリケーションと React の併用の場合は、ルート DOM ノードは複数持てる。
React 要素とルート DOM ノードをレンダーに渡す。
<div id="root"></div> <script type="text/babel"> const element = <h1>Hello, world</h1>; ReactDOM.render(element, document.getElementById('root'));
レンダリングされたページを inspect で調べると <div></div>
の間に <h1>Hello, world</h1>
が挿入されています。
要素の更新
React 要素はイミュータブル。生成後は、子要素、属性を変更できない。特定の時点のUIを表す。
更新するには、新しい要素を作成して ReactDOM.render()
に渡す必要がある。
function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render(element, document.getElementById('root')); } setInterval(tick, 1000);
秒刻みで表示する時分秒のみが更新される。
任意の時点で UI がどのように見えるべきかを考える。バグを排除することができる。
コンポーネントと props
props
を受け取り、React 要素を返す。
関数コンポーネントとクラスコンポーネント
コンポーネント定義の最もシンプルな方法は、関数を書くこと。
function Welcome(props) { return <h1>Hellom, {props.name}</h1> }
データの入ったprops
オブジェクトを引数に取り、 React 要素を返す。
ES6クラスを使用してコンポーネントを定義することもできる。
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1> } }
コンポーネントのレンダー
ユーザ定義のコンポーネントもReact 要素です。
const element = <Welcome name="Sara" />
属性と子要素を単一のオブジェクトとしてコンポーネントに渡す。このオブジェクトを props
と呼ぶ。
const Welcome = ({ firstName, lastName }) => { return ( <h1> Hello, {firstName} {lastName} </h1> ); }; const element = <Welcome firstName="Sara" lastName="Conner" />; ReactDOM.render(element, document.getElementById('root'));
Hello, Sara Conner
と表示される。
- 要素
<Welcome firstName="Sara" lastName="Conner" />
を引数としてReactDOM.render()
を呼び出す - React は
Welcome
コンポーネントを呼び出し、props
として{firstName: 'Sara', lastName: 'Conner'}
を渡す - コンポーネントは出力
<h1>Hello, Sara Conner</h1>
を返す - ReactDOM はコンポーネント出力に一致するように、DOM を更新
組み合わせる
Welcome
コンポーネントを複数回参照し、レンダーする App
コンポーネントを作成する。
const Welcome = ({ firstName, lastName }) => { return ( <p> {firstName} {lastName} </p> ); }; const App = () => { return ( <div> <Welcome firstName="Elijah" lastName="Wood" /> <Welcome firstName="Ian" lastName="McKellen" /> <Welcome firstName=" Orlando" lastName="Bloom" /> <Welcome firstName="Alan" lastName="Howard" /> </div> ); }; ReactDOM.render(<App />, document.getElementById('root'));
抽出
コンポーネントを分割する。
function Comment(props) { return ( <div className="Comment"> <div className="UserInfo"> <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} /> <div className="UserInfo-name">{props.author.name}</ div> </div> <div className="Comment-text">{props.text}</div> <div className="Comment-date">{formatDate(props.date)}</ div> </div> );
Avator
を抽出する。
function Avator(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); }
UserInfo
を抽出する。
function UserInfo(props) { return ( <div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name"> {props.user.name} </div> </div> ); }
すっきりした 'Commnet` コンポーネント。
function Comment(props) { return ( <div className="Comment"> <UserInfo user={props.author} /> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); }
コード全体
function formatDate(date) { return date.toLocaleDateString(); } /* Avatar Component */ function Avatar(props) { return <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} />; } /* UserInfo Component */ function UserInfo(props) { return ( <div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name">{props.user.name}</div> </div> ); } /* Comment Component */ function Comment(props) { return ( <div className="Comment"> <UserInfo user={props.author} /> <div className="Comment-text">{props.text}</div> <div className="Comment-date">{formatDate(props.date)}</div> </div> ); } /* comment */ const comment = { date: new Date(), text: 'I hope you enjoy learning React!', author: { name: 'Hello Kitty', avatarUrl: 'https://placekitten.com/g/64/64', }, }; ReactDOM.render( <Comment date={comment.date} text={comment.text} author={comment.author} />, document.getElementById('root') );
かわいいネコちゃんの画像とともに文字列と年月日が表示される。画像がかわいいとコードの凶悪さが倍増です。
コードを読み取りに挑戦!
function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); }
Avatar
コンポーネントは、画像を表示する。画像 URLは {props.user.avatarUrl}
。 alt属性
が {props.user.name}
ここの user
は、UserInfo
コンポーネントの <Avatar user={props.user} />
としているから。
実際は, comment
オブジェクト
const comment = { date: new Date(), text: 'I hope you enjoy learning React!', author: { name: 'Hello Kitty', avatarUrl: 'https://placekitten.com/g/64/64', }, };
の author: { name: 'Hello Kitty', avatarUrl: 'https://placekitten.com/g/64/64'}
が代入される。
ブラウザの inspect
で確認すると <img class="Avatar" src="https://placekitten.com/g/64/64" alt="Hello Kitty">
となっている。
UserInfo
コンポーネントで user={props.user}
とすると comment
オブジェクトの author
と紐づくのか?
Comment
コンポーネントで <UserInfo user={props.author} />
としているからです。
そのまま author
を使えば良いのにと考えますが、公式で全否定されます。
コンポーネントが使用される文脈ではなく、コンポーネント自身からの観点で props の名前を付けることをお勧めします。 https://ja.reactjs.org/docs/components-and-props.html#extracting-components
私なら、 <UserInfo avatar={props.author} />
とするだろうな。
度し難し!
comment
オブジェクトの author
が user
に置き換わるところを理解できれば、その他のコード理解は難しくない。めんどくさいだけ!
再利用できるコンポーネントをパレットとして持っておくことは、アプリケーションが大きくなれば努力に見合った利益を生みます。
逆に初心者が数時間で作れるようなアプリケーションならこだわることはないとも読めますね。
Props は読み取り専用
コンポーネントは、自身の props を変更してはいけない。
純粋関数 : 同じ入力に対し同じ結果を返す関数のこと
function sum(a, b) { return a + b; }
以下の関数は純粋関数ではない。
function withdraw(account, amount) { account.total -= amount }
React コンポーネントは、自己の props に対して純関数のように振る舞わねばなりません。 https://ja.reactjs.org/docs/components-and-props.html#props-are-read-only
state とライフサイクル
秒刻みの時計の例では、 ReactDOM.render()
を呼び出し、 UI を更新した。
function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render( element, document.getElementById('root') );} setInterval(tick, 1000);
再利用可かつカプセル化された Clock
コンポーネントを定義する方法を学ぶ。
ます時計の見た目をカプセル化する。
function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>It is {props.date.toLocaleTimeString()}.</h2> </div> ); } function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('root') ); } setInterval(tick, 1000);
Clock
がタイマーを設定して UI を更新するという処理は、カプセル化されるべき。
Clock
コンポーネント自身に更新させたい
ReactDOM.render( <Clock />, document.getElementById('root') )
これを実装するには Clock
コンポーネントに state
を追加する必要がある。
state には、そのコンポーネント固有のデータが含まれており、これは時間の経過とともに変化する可能性があります。state はユーザ定義のものであり、プレーンな JavaScript オブジェクトでなければなりません