Reactがマジでわかんない
おそらく買った本がむずすぎた
公式を読んで見ることにした
その私的まとめ
JSXの導入
const element = <h1>Hello, world!</h1>;
javascriptの拡張言語
使う理由はjavascriptにHTMLを書くようにかけるので直感的にわかりやすい
式の書き方
{}
の中に式を書けばいい
JSXは式だだからif
にもfor
にも打ち込める
render
<div id="root"></div>
上記がHTMLファイルにあるとするとこれルートDOMを言うことにする
React要素をルートDOMにレンダリング(HTMLを人が見れる形にする)するには
React.render()に渡す
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
引数 | 意味 |
---|---|
第1 | React要素 |
第2 | ルートDOM |
Reactは意味ミュータブル
更新するには新しい要素を作成してReact.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);
もう一度いうと更新するには
新しく要素を作詞→React.render()に渡す、という流れ
React は必要な箇所のみを更新する
これを見たとき楽しくなった。とある本を読んで勉強するよりドキュメント読んだほうが遥かに楽しいし勉強になる。
今後は「入門」という文字の入った本に気をつけよう
話を戻すと先の時計だが、ディベロッパツールで見てみると時刻のとこだけのDOMを変更している
これはとある入門書を見て知っていたが百聞は一見にしかずとはまさにこのこと
理解ができた感動した
componentとprops
コンポーネントで部品を独立したものにして再利用可能にできる
コンポーネントはjavascript関数に似ていてpropsという入力を受け取りReactエレメントを返す
コンポーネントはReactエレメントを返す。
コンポーネントはReactエレメントを返す。
コンポーネントはReactエレメントを返す。
そうだったのか。。
コンポーネントはReactエレメントを返すのか。。
コンポーネントの定義には関数とクラスがある、これは知ってる
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
上記コードの説明も丁寧でわかりやすかった
このときコンポーネント名は大文字で始める
import React from 'react';
import ReactDOM from 'react-dom';
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
このコードを見たとき更新かなと思ったが、3行表示された、ああそうだったと、忘れてるなぁと思った
コンポーネント抽出
unction 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>
);
}
上記コードがコードの分割により以下のようになる
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 Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
見やすい、スッキリ
Avatarコンポーネントの作成
このコンポーネントは自身がレンダリングされることを想定していない
propsの名前がuserがるかわれているのは
これはコンポーネント自身の観点で名前をつけることが推奨されているから
長いコードを関数化する作業に似ている
propsは読み取り専用
propsは変更してはいけない
Reactコンポーネントはpropsに対して純粋関数であること
stateとライフサイクル
先の秒刻み時計で進めていく
以下からは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自身を更新させたいらしい
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
これの実現にはstate
を追加する必要があるという
このstateはコンポーネントによって完全プライベートなもの
関数をクラスに変換
以下の手順でクラスに変換
- React.Componentを継承
- render()というからメソッドを用意
- 関数の中身をそのメソッドの中に書く
- render()内のpropsをthis.propsへ書き換える
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
renderメソッドは更新が発生したときに毎回呼ばれるが同一DOMノード内で<Clock />
をレンダーしている限り、Clockクラスのインスタンスは1つだけ使われる
つまり同一DOM無いであればいくらrenderメソッド内のClockを呼び出しても同じインスタンスが使われる、保持したものはそのまま使えるということ、かな
クラスにローカルなstateを追加
以下の3ステップでdateをpropsからstateに移す
- render()メソッド内の
this.props.date
をthis.state.date
に書き換える - this.stateの初期状態を設定するクラスコンストラクタを設定
-
<Clock />
要素からdateプロパティを削除
2
では親のコンストラクタへのpropsの渡し方に注意
クラスコンポーネントでは常にpropsを引数として親クラスのコンストラクタを呼び出す必要がある
super(props)
とある入門書ではなぜこれを書くのか。
初見でこれを見たときなんで親クラスのを呼び出しているのかわからなかった。
今もなぜだかわからないけどそうおいうものだとわかっただけもスッキリする。
クラスにライフサイクルメソッドを追加
コンポーネント破棄時にリソースを開放したほうがいい
タイマー設定は最初にClockが描画(マウント)されるとき。
タイマークリアはClockが生成したDOMが削除(アンマウント)されるとき
このようなライフサイクルメソ度を使っていく
componentDidMount
コンポーネントがマウントされた直後に呼ばれる
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
タイマーIDをthis上に格納しているのには訳がある
componentWillUnmount
コンポーネントがアンマウントされた直後によばれる
このタイミングでクリアするのがいい
componentWillUnmount() {
clearInterval(this.timerID);
}
最後にtick()を作る
コンポーネントがローカルstateの更新をするためにthis.setStateを使う
tick() {
this.setState({
date: new Date()
});
}
何が起きたかの振り返り
-
<Clock />
がReactDOM.render()に渡されると.ReactはClockコンポ-ネントのコンストラクタを呼び出す。このときにstateを初期化。 - 次にReactはrender()を呼び出す。
- Clockの出力されるとReactはcomponentDidMountを呼び出す。
- tick()が実行されるとsetStateでstateが変更される。それをReactが感知してrender()を再度呼び出す。そしてthis.state.dataが前回と違っていいるためReactはDOMを更新
- Clockコンポーネントが削除されたらcomponentWillUnmountでタイマーが止まる
stateを正しく使用する
stateを直接変更しない
以下は再レンダーされない
this.state.comment = 'Hello';
setStateを使えばいい
this.setState({comment: 'Hello'});
直接書き換えた場合、再レンダリングされない
Reactが変更を検知できないため。