前情提要:
昨天我們建立了一個Button component,也在裡面偷渡了props的用法(昨日傳送門)。了解了component組合之一的Function Component之後,今天就來介紹Class Component吧,也可以一起把state跟大家介紹。
Function Components轉成Class Component是容易做到的,但要由Class Components轉成Function Components就有一點難度了,原因有二:
state
,但Function Component不行生命週期(lifecycle)
,可以在Component掛載、更新或卸載等不同階段阻擋做一些事...對...一些你要托付它的事。我們今天先來講class和state,明天再來好好認識生命週期(lifecycpe)。
這人每天都在埋伏筆好煩啊!
官方教學無痛轉換五步驟,教你如何從function轉成class:
React.Component
的擴展子類別render
的empty method內容
移到render裡this.props
// class component
class Button extends React.Component { // 1. class Xxx extends React.Component {}
render() { // 2. render() {...}
return ( // 3. return {...}
(<div>{this.props.text}</div>) // 4. this.props ...
);
}
}
// function component 5. 移除以下程式碼
cnost Button = (props) => {
return (
<div>{props.text}</div>
);
}
還記得我們在開始的時候有提到兩個class component轉function component困難的原因之一嗎?Class Components允許擁有state,但Function Component不行
所以state在component內該怎麼使用呢?
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { // 初始化 state
text: 'hahaha'
}
}
render() {
return (<div>{this.state.text}</div>); // 使用 state
}
}
在上面的程式碼中,我們透過在constructor(建構子))
裡建立的this.state
初始化我們的state資料,然後再透過this.state.text
取得text的值。
等等!那個constructor(建構子)是什麼?如果不認得的話大概就是Javascript沒學好,畢竟連React官網都直接放MDN Constructor的link了QAQ,以下簡單整理:
super
來呼class的父類別(super class)另外,關於constructor(),React是這麼說的,如果沒有用到state也不用bind methods的話,我們也不需要使用constructor了,下面附上原文出處讓大家參考:
If you don’t initialize state and you don’t bind methods, you don’t need to implement a constructor for your React component.
出處:React.Component-constructor
那constrouctor和super內的props又是怎麼一回事?事情是這樣的,constructor是在react component被掛載(mounted)前就被呼叫的,所以不使用super(props)的話,props在constructor的範疇內會是undefined呦。
undefined情況
正常運作情況
補充:
Class components should always call the base constructor with props.
出處:State and Lifecycle
結論:要嘛就不要用constructor,要用constructor,props就是標配。
上面講了一大堆javascript範疇的東西,果然是基礎沒打好煩惱一大票...(自我感嘆)
接著我們來看看state在component內可以做些什麼吧!
以我們Button的例子來看,如果今天我...想做一個計數器(弱爆ˊ_>ˋ),外部不需要傳資料進來,我只需要更新內部數量就好,那要怎麼實作呢?
需求
首先,在src/components下建立一個新的counter.js,我們把component獨立出來
count初始值為0
(設定count來記錄我們的點擊次數)h1 element
和可供點擊的button element
點擊事件(click event)
更新countaddCount
的function處理click eventimport React, { Component } from "react";
class Counter extends Component { // 1. 建立Class Component
constructor(props) { // 2. 初始化state並指定count初始值為0
super(props);
this.state = {
count: 0
};
this.addCount = this.addCount.bind(this); // 6. 綁定component instance
}
addCount() { // 5. 處理click event
this.setState({ count: this.state.count + 1 });
}
render() { // 3.
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.addCount}>點擊+1</button> {/* 4.設定點擊事件 */}
</div>
);
}
}
export default Counter;
然後我們回到index.js匯入我們的Counter component
import React from 'react';
import ReactDOM from 'react-dom';
import Counter from './src/components/counter'; // 匯入Counter
const App = () => {
return (
<div>
<Counter/> { /* 使用Counter component */}
</div>
);
};
ReactDOM.render(<App/>, document.getElementById('app'));
在terminal輸入yarn start
,開啟localhost:8080,操作畫面如下:
以上就是state的用法了!完整程式碼請參照github傳送門
補充之一
剛剛bind(綁定)function到instance的做法是Bind in Constructor (ES2015)
另外React還介紹了其他方法>How do I bind a function to a component instance?
以下列出記錄一下
Bind in Constructor (ES2015)
class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
Class Properties (Stage 3 Proposal)
class Foo extends Component {
// Note: 此語法還是實驗性,尚未標準化
handleClick = () => {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
以下兩種bind方法會在每次render的時候就建立一個新的function,有效能上的影響
Bind in Render
class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick.bind(this)}>Click Me</button>;
}
}
Arrow Function in Render
class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={() => this.handleClick()}>Click Me</button>;
}
}
補充之二 2018/10/14更新
你該知道的三件關於setState()的大小事
this.state.comment = 'Hello';
this.setState({comment: 'Hello'});
object
或function
)// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
正確用法 第一個參數為上次收到的state
;第二個參數為當時更新的props
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
總結今日:
state
生命週期(lifecycle)
本日完結,有任何錯誤再請大家指正。