# 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>
)
}
}
~~~
运行后可以在控制台看见打印的实例:
![](https://img.kancloud.cn/f3/df/f3dfc8561b412d877b015b43208778b1_994x272.png)
也就是在自定义组件,继承自`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
~~~
![](https://img.kancloud.cn/ae/19/ae19201f3f468faa9d9782ffe8fa7253_290x218.png)
点击按钮可以将用户年龄增加。值得注意的是,这里使用`this.setState`来更新的时候,并不需要将没有发生改变的用户`name`字段重新赋值。这里也可以发现,`setState`不是覆盖,而是对比差异,然后更新。