# 1. setState的簡單實踐和理解
比如說,我們需要定義一個列表組件。當點擊奇數列表的時候隨機調換一下順序;當點擊偶數列表的時候在其后加=和不加=號之間切換。
## 1.1 代碼實現
~~~html
<body>
<div id="test"></div>
<script crossorigin src="./js/react.development.js"></script>
<script crossorigin src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
var root = document.getElementById("test")
class MyElement extends React.Component {
constructor() {
super();
this.state = {datas: ["張三", "李四", "王五"]}
}
userClick(index) {
let flag = index % 2 === 0
if (flag) { // 奇數列表項亂序切換
let temp_list = []
for (let i = 0; i < this.state.datas.length; i++) {
if (i % 2 === 0) {
temp_list.push(this.state.datas[i])
}
}
temp_list.sort(() => Math.random() - 0.5)
let temp_data = []
let j = 0
for (let i = 0; i < this.state.datas.length; i++) {
if (i % 2 === 0) {
temp_data.push(temp_list[j])
j++
} else {
temp_data.push(this.state.datas[i])
}
}
this.setState({datas: temp_data})
} else { // 等號和沒等號之間切換
let temp_list = []
let switchFlag = false
if(this.state.datas[1] != null && this.state.datas[1].endsWith("=")) {
switchFlag = true
}
for (let i = 0; i < this.state.datas.length; i++) {
if (i % 2 !== 0) {
if (!switchFlag) {
temp_list.push(this.state.datas[i] + "=")
} else {
temp_list.push(this.state.datas[i].substring(0, this.state.datas[i].length - 1))
}
} else {
temp_list.push(this.state.datas[i])
}
}
this.setState({datas: temp_list})
}
}
render() {
return (
<ul>
{
this.state.datas.map((item, index) => {
return <li key={index} onClick={() => {
this.userClick(index)
}}>{item}</li>
})
}
</ul>
)
}
}
ReactDOM.render(<MyElement/>, root)
</script>
</body>
~~~
注意到這里更新數據的時候使用了:
```
this.setState({datas: temp_list})
```
且數據聲明也放置在了構造器中:
```
constructor() {
super();
this.state = {datas: ["張三", "李四", "王五"]}
}
```
那么,為什么需要這么寫?
## 1.2 分析
對于下面極簡的類組件:
~~~
class MyElement extends React.Component {
constructor() {
super();
console.log(this)
}
render() {
return (
<div>Hello!</div>
)
}
}
~~~
運行后可以在控制臺看見打印的實例:

也就是在自定義組件,繼承自`React.Component`的時候,默認就帶有一個為`null`的屬性`state`。如果我們需要做到數據的動態更新,就需要使用`state`這個屬性,并且在使用的時候,需要使用`setState`來進行更新。
# 2. 函數組件state、類組件state
## 2.1 函數組件state
比如下面定義一個簡單的函數組件:
~~~
function Demo() {
return (
<div>Hello!</div>
)
}
~~~
經過`babel`翻譯后為:
```
"use strict";
function Demo() {
return /*#__PURE__*/React.createElement("div", null, "Hello!");
}
```
我們知道在`Rect`函數組件中只有`props`,沒有`refs`以及`state`。如果需要使用`state`需要引入外部的鉤子。比如:
~~~
function Demo(props) {
console.log(props)
const [flag, setFlag] = useState(false)
const handleClick = () => {
setFlag(!flag)
}
return (
<div onClick={handleClick}>Hello! flag is {flag ? 'true': 'false'}</div>
)
}
root.render(
<Demo name={"張三"} />
);
~~~
在申明`state`變量的時候,就指定了變量名和更新這個值狀態的方法,即:
> const [flag, setFlag] = useState(false)
## 2.2 類組件state
~~~
import React from "react";
class ClassState extends React.Component{
constructor() {
super();
this.state = {name: '李四', age: 12}
}
handleClick = () => {
const pre_age = this.state.age
this.setState({age: pre_age + 1})
}
render(){
const {name, age} = this.state
return (
<div>Hello! class state. Info: <br/>
<p>姓名:{name}</p>
<p>年齡:{age}</p>
<button onClick={this.handleClick}>點擊修改</button>
</div>
)
}
}
export default ClassState
~~~

點擊按鈕可以將用戶年齡增加。值得注意的是,這里使用`this.setState`來更新的時候,并不需要將沒有發生改變的用戶`name`字段重新賦值。這里也可以發現,`setState`不是覆蓋,而是對比差異,然后更新。