https://ja.reactjs.org/docs/state-and-lifecycle.html
https://qiita.com/morrr/items/c32a4916d55373b64c70
https://mae.chab.in/archives/2956
こちらを参考にしながら今日も勉強いたします!
[前回のRect勉強録](https://qiita.com/irico/items/d1c90f0f905635a6529e)
##タイマーの分割
function tick() {
const element = (
<div>
<h2>タイマー</h2>
<p>{new Date().toLocaleTimeString()}.</p>
</div>
);
ReactDOM.render(
element,
document.querySelector('body')
);
}
setInterval(tick, 1000);
setIntervalで1秒ごとにtickコンポーネントを更新しています。
これを分割したいのですが、今まで学んだことを使うとこうなります。
function Clock(props){
return(
<div>
<h2>タイマー</h2>
<p>{props.date.toLocaleTimeString()}.</p>
</div>
);
}
function tick(){
ReactDOM.render(
<Clock date={new Date()} />,
document.querySelector('body')
);
}
setInterval(tick, 1000);
でもこれだと、時間処理を毎秒更新する機能をClockに持たせることができていません。
Clockが自身を更新できるようにしたいです。
そのためにはまず関数コンポーネントからクラスコンポーネントに書き換えます。
class Clock extend React.Component {
render(){
return(
<div>
<h2>タイマー</h2>
<p>{this.props.date.toLocaleTimeString()}.</p>
</div>
);
}
}
クラス変換で追加したrenderメソッドは更新が発生するたびに呼ばれます。
##state
state・・・内部で状態を保持できる。
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h2>タイマー</h2>
<p> {this.state.date.toLocaleTimeString()}.</p>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.querySelector('body')
);
・クラスコンポーネントの場合、引数をpropsにして、super(props)を呼び出す。
・stateの初期状態をコンストラクタで設定する。
これでdateの情報をClockがもてるようになりました。
しかし、時間の更新の機能もコンポーネントに持たせたいですね。
##ライフサイクルイベント
まずは用語から。
マウント・・・コンポーネントが最初に描画される時。
アンマウント・・・コンポーネントから生成したDOMが削除される時。
上記のような状態の変化(ライフサイクル)に合わせて発火するライフサイクルメソッドがReactには準備されている。
メソッド名 | 発火地点 |
---|---|
componentWillMount() | マウントされる直前 |
componentDidMount() | マウントされた直後 |
componentWillReceiveProps() | propsが変化した時 |
shouldComponentUpdate() | state,propsが変更され再描画が走る前。trueで再描画される。 |
componentWillUpdate() | 再描画される前 shouldComponentUpdate()がfalseで発火しない |
componentDidUpdate() | 再描画されたタイミング 再描画後のDOMにアクセス可 |
componentDidMount(){
this.timerID = setInterval(
()=> this.tick()
こちらはマウント直後に発火します。
tick()
メソッドは後ほど記述します。
this.props
やthis.state
以外なら、this上に任意のデータを格納してOKです。
componentWillUnmount() {
clearInterval(this.timerID);
}
こちらはマウントされる直前に発火します。
これで前のsetIntervalをクリアできますね。
tick(){
this.setState({
date:new Date()
});
}
setState
・・・class componentのメソッド(なのでthisで呼び出せる)
第一引数には更新後のstateの値をオブジェクト or 関数で渡す。
なので、流れとしては、
- componetDidMount()で1秒毎にtick()を呼ぶようにセット
- tick()でstateのdataを新しい時間になるように変更
- stateの変更によってrender()が走る。時間が描写される。
- 今後DOMからClockが削除される場合は、componentWillUnmount()が走ってタイマーが停止する。
ちょっとややこしいのでゆっくり追って理解しました。
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h2>タイマー</h2>
<p>{this.state.date.toLocaleTimeString()}.</p>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.querySelector('body')
);
全部まとめるとこうですね。
ちゃんとタイマー機能をClockに分離することができました。
##stateについてもっと知る
###禁忌!
stateを直接変更してはならない。
this.state.animal = '犬'; //ダメ
this.setState({animal:'犬'}); //正しい
コンストラクター以外ではsetState()を使うこと!
###非同期に注意!
state・propsの更新は非同期に行われることがあるのでそれに注意しないといけない
→this.porpsとthis.state同士を依存させるのはダメ
this.setState({
sum: this.state.sum + this.props.increment,
}); //非同期だからうまく値が取れない場合が
この場合、setState()
を以下の記述方法にする。
this.setState((state,props)=>({
sum: state.sum + props.increment
}));
疑問...内部でどんな処理が行われて非同期を回避しているんでしょう。
いつか調べたいと思います。
今日はちょっとドキュメント読み解きに手こずったので間違いがあるかもしれません,,,
ご指摘等お気軽にお願いします!