uncategorized

狀態 (state)

狀態(state)和屬性(props)類似,都是一個元件所需要的一些數據資料集合,但是 State 是私有的,它不能被自身元件以外的任何元件使用,

React 把元件看成是一個狀態機,如果元件沒有狀態,那就只需要寫成函式元件並透過 props 傳遞資料,不需要寫成類別元件,但如果需要與使用者進行互動,就必須使用 State,先由 State 初始化元件本身的資料,再由 setState 方法改變元件的資料,進而渲染改變過後的 DOM 元素。

無狀態需要改變: 函式元件

當我們並不須跟使用者互動時,我們的元件只需寫成函式元件就好,也就是說,我們只要寫一個 function component,並用 props 傳遞資料給子元件即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function TheLast (props) {
return(
<div>
<h2>I am {props.name} and {props.age} years old.</h2>
<h2>My partner is {props.partner}</h2>
</div>
)
}
const element = <TheLast name="Joel" age="55" partner="Ellie" />

ReactDOM.render(
element,
document.getElementById("root")
)

// I am Joel and 55 years old.
// My partner is Ellie

但如果我們想要改變元件的資料,我們就必須寫成類別元件。

有狀態需要改變: 類別元件

上面的函式元件如果我們想要改變姓名(Joel)與年齡(55),我們就要改成類別元件。

首先,function TheLast(props) 改成 class TheLast extends React.Component
function component 的 state 要寫在 constructor() 裡。
state 是個物件,所以狀態的資料必須以物件形式撰寫,例如 this.state = {…}
看到了嗎?我們用了 this.state,super()是用來初始化 this 的,可以綁定事件到 this 上。
所以我們的 constructor 會變成以下這樣:

1
2
3
4
5
6
7
8
constructor(){
super()
this.state = {
name: 'Joel',
age: 55,
partner: 'Ellie'
}
}

接下來就單純多了,在 function component 裡面我們用 return 顯示 JSX 結構,但是 class component 則用 render 方法把 return 包住,所以整個重新改寫的元件會長這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class TheLast extends React.Component {
constructor(){
super()
this.state = {
name: 'Joel',
age: 55,
partner: 'Ellie'
}
}
render(){
return (
<div>
<h2>I am {this.state.name} and {this.state.age} years old.</h2>
<h2>My partner is {this.state.partner}</h2>
</div>
)
}
}

setState

接著我們進入本篇的重點,setState。setState 的工作除了更新 this.state 之外,還要負責觸發重新渲染(re-render)。

讓我們寫一個能改變 state 的 function 並命名為 switchNameHandler1,裡面用 this.setState 設置新的 state value,這裡有兩個重點:

要改變狀態一定要用 this.setState 而非 this.State,後者只用在狀態的初始化。
this.setState 是函數,可接受一個 function 或 object 作為參數,在此例我們用 object 做為參數。

1
2
3
4
5
// setState 語法
setState(
function | object nextState,
[function callback]
)

在這兒我們想要用 setState 把 Joel 跟 55 換掉,變成 George 跟 20,所以我們把 this.state 想改變的值寫在 this.setState 的 object 裡,於是 switchNameHandler1 就可寫成以下程式碼:

1
2
3
switchNameHandler1(){
this.setState({name: 'George', age: 20})
}

然後在 JSX 裡面插入一個 button,並且安置一個 onClick 事件並用花括號包住 setState 的 function name。

1
<button onClick = {this.switchNameHandler1}>Change state</button>

此時的程式碼會長這樣子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class TheLast extends React.Component {
constructor(){
super()
this.state = {
name: 'Joel',
age: 55,
partner: 'Ellie'
}
}
switchNameHandler1(){
this.setState({name: 'George', age: 20})
}
render(){
return (
<div>
<h2>I am {this.state.name} and {this.state.age} years old.</h2>
<h2>My partner is {this.state.partner}</h2>
<button onClick = {this.switchNameHandler1}>Change state</button>
</div>
)
}
}
ReactDOM.render(
<TheLast />,
document.getElementById('root')
)

但你這時會發現按鈕並沒有作用,這是因為我們沒有把 this 綁定在 switchNameHandler1 上,所以必須要在 constructor 裡多加一行:

1
this.switchNameHandler1 = this.switchNameHandler1.bind(this)

這時的 button 就有 setState 的功能了。但是我覺得這樣多寫一行很髒,所以我會用 arrow function 綁定 this 的特性,把 switchNameHandler1 用 arrow function 改寫:

1
2
3
switchNameHandler1 = () => {
this.setState({name: 'George', age: 20})
}

完整程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class TheLast extends React.Component {
constructor(){
super()
this.state = {
name: 'Joel',
age: 55,
partner: 'Ellie'
}
// this.switchNameHandler1 = this.switchNameHandler1.bind(this)
}
switchNameHandler1 = () => {
this.setState({name: 'George', age: 20})
}
render(){
return (
<div>
<h2>I am {this.state.name} and {this.state.age} years old.</h2>
<h2>My partner is {this.state.partner}</h2>
<button onClick = {this.switchNameHandler1}>Change state</button>
</div>
)
}
}
ReactDOM.render(
<TheLast />,
document.getElementById('root')
)

setState 的其他特性

  • this.state 不會在 this.setState 調用之後立刻更新
  • 連續調用多次 this.setState,都只會觸發一次生命週期的更新

參考連結:
React 16 - The Complete Guide
深入理解React 组件状态(State)
React 回憶錄(四)React 中的狀態管理
React.Component
状态(State) 和 生命周期
React JS State and Props
setState:这个API设计到底怎么样

Share