React组件三大核心属性 - State
# 一、state的理解
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
向组件中添加局部状态:
组件的局部状态(16.8版本之前)只存在于class声明的组件,函数声明的组件无state 所以我们称函数声明的组建为无状态组件,16.8版本后react 添加了Hook的概念让函数组件拥有了局部状态(无状态组件说法就被舍弃了)
提示
除了使用外部数据(通过 this.props 访问)以外,组件还可以维护其内部的状态数据(通过 this.state 访问)。当组件的状态数据改变时,组件会再次调用 render() 方法重新渲染对应的标记。
// 1、创建类组件
class ComponentA extends React.Component {
constructor(props) {
super(props);
// 初始化状态
this.state = {
name: '小明',
age: 18
}
}
render() {
// 读取状态
const { name, age } = this.state;
return (
<div>
<h2>我是组件A</h2>
<p>render函数就是我的渲染函数</p>
<p>name: {name}</p>
<p>age: {age}</p>
</div>
)
}
}
// 2、渲染组件到页面
ReactDOM.render(<ComponentA />, document.getElementById('test'));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
注意:
- 每个class都由constructor方法,当class代码中没有显示声明constructor构造函数时,class会自动隐式添加该方法
- 使用class继承的类,在其constructor构造函数中必须首先调用构造函数自带的super方法在继承的父类中完成this对象的塑造并继承父类的属性方法.如果不调用该方法子类将没有this对象
- 如果你的react class组件内部不需要创建state 绑定方法或者任何在constructor构造函数中要执行的代码推荐隐式创建constructor
# 二、react中的事件绑定
onClick 而不是 onclick,react中把方法是小写的都重写了一遍,比如onblur ==> onBlur
注意
onClick={}, 不能写成onClick={test()}因为页面进来执行render()之后会立即执行test方法,把undefined赋给了onClick,所以当你再次点击标题的时候是没有反应的,因为此时为undefined,react中如果遇到是undefined是不做任何动作的
class ComponentA extends React.Component {
constructor(props) {
super(props);
// 初始化状态
this.state = {
isHot: true
}
}
render() {
// 读取状态
const { name, age } = this.state;
return (
<div>
<h2>我是组件A</h2>
<p>render函数就是我的渲染函数</p>
<p>name: {name}</p>
<p>age: {age}</p>
<p onClick={test}>点我</p>
</div>
)
}
}
function test() {
console.log('我被点击了');
}
ReactDOM.render(<ComponentA />, document.getElementById('test'));
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
28
# 三、类中方法中的this
class Weather extends React.Component {
constructor(props) {
super(props);
// 初始化状态
this.state = {
isHot: true
}
}
render() {
// 读取状态
const { isHot } = this.state;
// changeWeather 作为 onClick 的回调,是直接原型链中找到存放起来,然后再赋值给onClick,,不是通过实例调用的,而是直接调用,所以 this 不指向实例
// return <h1 onClick={changeWeather}>今天天气很{isHot ? '炎热' : '凉快'}</h1> error: changeWeather is not defined
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉快'}</h1>
}
changeWeather() {
console.log(this); // undefined 因为changeWeather不是实例对象调用的
}
}
ReactDOM.render(<Weather />, document.getElementById('test'));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
问题一:定义的changeWeather方法是位于什么位置的?
答: Weather的原型对象上
,供实例使用,通过Weather实例调用changeWeather时,changeWeather中的this就指向Weather实例
问题二:其中的this是什么?
答: undefined
,因为类中定义的方法在局部自动开启了严格模式
,所以this为undefined
# 四、this指向问题
我们上面已经说了调用了方法之后this是undefined,那这个问题怎么解决呢?
这里使用bind() 这个方法,只需在constructor中改变方法的this指向问题
。
constructor(props) {
super(props);
// 初始化状态
this.state = {
isHot: true
}
// 改变this指向
this.changeWeather = this.changeWeather.bind(this);
}
2
3
4
5
6
7
8
9
问题:bind做了什么?
答: bind: 做了两件事情 ---- 生成新的函数并且改变this为Weather的实例对象;this.changeWeather是原型上的方法,通过bind改变this之后生成新的方法放在了实例自身上,导致了实例中也有changeWeather这个方法,这样就能进行调用了。
注意:
没有执行bind之前this为undefined,因为类中定义的方法在局部自动开启了严格模式,所以this为undefined
执行bind之后this指向Weather的实例对象
# 五、组件中局部状态更新问题:
我们打印React class组件的state发现,它并没有像Vue一样给每个state属性进行数据劫持,只是一个普通对象。这样就导致如果直接修改class 组件state是不会引起页面刷新的。
在React中修改局部状态state,必须使用this.setState()。
// Wrong 此代码不会重新渲染组件
this.state.age = 20;
// Correct
this.setState({age: 20});
2
3
4
5
this.setState是浅合并,也就是说this.setState({age})完整保留了this.state.name,但完全替换了this.state.age。你可以调用 setState() 独立地更新state中任意属性
# 六、setState的使用
这里其实说的就是第五点中的状态更新问题
- 状态里的数据不允许直接更改,要借助一个内置的API更改
- 状态必须通过setState进行更改,且更新是一种合并操作,不是替换
// Wrong 此代码不会重新渲染组件
this.state.age = 20;
// Correct
this.setState({
age: 20
});
2
3
4
5
6
7
# 七、State的简写
其实这个方式也可以解决上面提到的this为undefined的问题
但是有一点不同的是:没有使用构造器,方法改为箭头函数
,然后state我们直接写成一个对象,就像那个普通对象添加属性,有就进行替换,没有就新增。
class Weather extends React.Component {
// 初始化状态
state = { isHot: true, wind: '大风' }
render() {
const { isHot, wind } = this.state;
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}, {wind}</h1>
}
// 自定义方法 --- 要用赋值语句的形式+箭头函数
changeWeather = () => {
// 箭头函数本身没有this,this是外层的this,即Weather
const isHot = this.state.isHot;
this.setState({
isHot: !isHot
})
// console.log(this); // Weather
}
}
ReactDOM.render(<Weather />, document.getElementById('test'));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 八、小总结
组件中render方法中的this为组件实例对象
组件自定义的方法中this为undefined,怎么解决?
强制绑定this: 通过函数对象的bind() 【构造器写法】
赋值语句+箭头函数 【无构造器写法】
状态数据,不能直接修改或更新,必须通过setState进行更改