state
是 React 中元件的狀態,在元件內 state
是一個 object。state
又能分為內部狀態和外部狀態,內部狀態即前面所述的元件內的 object,外部狀態是指從元件外傳進來的資料狀態,外部狀態一般是透過第三方套件做資料管理。另外要注意的是,上一篇有提到過的 function component 本身是沒有生命週期的,也不能使用 state
,但是 function component 可以透過 react hook 來使用 state
及生命週期。
使用 state
只需要在 class
當中直接去定義即可,直接去定義一個 state
物件,這個物件代表了元件內的屬性狀態,而我們要使用 state
時候可以透過 this.state
調用,如下面的範例,我們在 UserProfile
這個元件中定義了 state
,並設定 userName
屬性,在 JSX 中調用時,如果沒有這個屬性,就判斷返回 null
。
class UserProfile extends React.Component {
state = { //定義 state
userName: Leo
}
render () {
return <div>
{ this.state.userName ? this.state.userName : null}
</div>
}
}
若是開發項目本身不支援直接定義 class
屬性的方式,就必須使用 constructor
構造函式(官方的 create-react-app 是支援的)。
class UserProfile extends React.Component {
constructor () {
super();
this.state = { //定義 state
userName: Leo
}
}
render () {
return <div>
{ this.state.userName ? this.state.userName : null}
</div>
}
}
setState
是 class component 本身的一個 function,用於更新元件的狀態。setState
是非同步的,接受兩個參數 setState(stateObject, callback)
。
state
更新後的狀態,函式的話是返回一個物件,也是代表更新後的 state。setState()
函式執行的時候,裡面是去塞入要更新的 state
值。以下面的範例來說,我們先在 state
中定義一個 count
屬性,在元件中設定一個 addCount
函式,每次執行這個函式就會去更新 state
中的 count
,讓它 +1。我們在 JSX 中呼叫 count
,並設置一個 button
用 onClick
事件去綁定 addCount
函式,這樣每次點擊 button
就會更新 count
。我們也可以將更新的函式直接寫在 onClick
中,這樣就不必綁定 this
。
class App extends React.Component {
state = {
count: 0
}
addCount () {
const { count } = this.state; //取得 this.state.count 的值,並賦到新的 count 變數上
this.setState((
count: count + 1 //將新的 count +1 後賦給 state 中的 count
))
}
render () {
// 在 button 上利用 onClick 綁定 addCount 事件
const { count } = this.state;
return <div>
<div>{ count }</div>
<button onClick={this.addCount.bind(this)}></button>
<button onClick={() => {
const { count } = this.state;
this.setState({
count: count + 1
})
}
}>click</button>
</div>
}
}
state
也可以經由 setState()
做雙向綁定,一般是應用在表單輸入方面,例如下面的 input
,利用 onChange
來偵測 value
的變化,如果有變化,就用 setState
去更新 state
。
class App extends React.Component {
state = {
value: ''
}
render () {
const { value } = this.state;
return <div>
<input
type="text"
value={ this.state.value }
onChange={ e => this.setState({ value: e.target.value })}
/>
<p>輸入的值為:{ value }</p>
</div>
}
}
前面有提到 state
的更新是非同步的,如下面的範例,兩個 setState()
是同時執行的,所以當我們點擊 button
後,count
會因為加 1 而變為 1,但是 age
則不會變,因為兩個 setState()
是同時執行,所以第二個 setState()
執行時,count
還是 0,因此 age
不會改變。
class App extends React.Component {
state = {
name: 'leo',
age: 18,
count: 0
}
changeData () {
const { age, count } = this.state
//由於是非同步,兩個 setState 會同時執行
this.setState({ count: count + 1}); //count 變為 1
this.setState({ age: age + this.state.count}); // 跟上一行同時執行,執行時 count 還是 0,所以 age 數字不變
}
render () {
const { name, age, count } = this.state;
return <div>
{name} {age} 歲 || {count}
<button onClick={this.changeData.bind(this)}>change</button>
</div>
}
}
但有些情況下我們會一次更新多個 state
,這種時候通常會希望一個 state
更新完再去執行下一個 state
的更新。這種時候就會用到前面提過的 setState
的另一個參數 callback
。我們在第一個 setState
的回調函數中去執行第二個 setState
。
class App extends React.Component {
state = {
name: 'leo',
age: 18,
count: 0
}
changeData () {
const { age, count } = this.state
// 在 callback 中去執行第二個 setState
this.setState({ count: count + 1}, () => {
this.setState({ age: age + this.state.count});
});
}
render () {
const { name, age, count } = this.state;
return <div>
{name} {age} 歲 || {count}
<button onClick={this.changeData.bind(this)}>change</button>
</div>
}
}
但上面的這種寫法,一但 setState
很多,就會陷入以前常在非同步處理中見到的 callback hell,維護上也不是很直覺。因此有了利用前面章節提過的 async
await
來搭配處理的方式,讓我們在開發和維護上都更簡潔易懂。
state = {
name: 'leo',
age: 18,
count: 0
}
async changeData () {
const { age, count } = this.state
await this.setState({ count: count + 1});
await this.setState({ age: age + this.state.count}); // 上一行執行完才執行這一行
}
render () {
const { name, age, count } = this.state;
return <div>
{name} {age} 歲 || {count}
<button onClick={this.changeData.bind(this)}>change</button>
</div>
}
我們也可以透過 Promise
搭配 async
await
來處理同步及非同步問題。
class UserProfile extends React.Component {
state = {
name: 'leo',
age: 18,
count: 0
}
// 先用 Promise 來包裝好 setState()
changeVlue (state) {
return new Promise((resolve) => {
this.setState(state, resolve)
})
}
// 利用 async await 直接調用 Promise
async changeData () {
const { age, count } = this.state
await this.changeVlue({count: count + 1})
await this.changeVlue({age: age + this.state.count}); // 上一行執行完才執行這一行
}
render () {
const { name, age, count } = this.state;
return <div>
{name} {age} 歲 || {count}
<button onClick={this.changeData.bind(this)}>change</button>
</div>
}
}
由於 state
和 props
部分要注意的比較多,因此我分成兩篇來整理(其實是因為後面文章來不及寫了)。下一篇會整理 props
相關的概念。