文章目录
- 1 state简介
- 2 初始化state
- 3 React中事件绑定
- 4 类方法中this指向问题
- 5 解决React类组件方法this指向
- 5 setState
- 5.1 更新state状态数据方式
- 5.2 更新state整体替换还是合并相同项?
- 5.3 关于构造器、render方法调用次数
- 6 state简写
- 7 问题解答
- 后记
1 state简介
React中没有state(状态)的组件称为简单组件;有state称为复杂组件。
- 理解
- state是组件对象最重要的属性之一,值是对象(1个或者多个key-value组合)
- 组件被称为状态机,通过更新组件的state来更新对应页面显示(重新渲染组件)
下面我们来一步一步认识state,并最后给出日常开发中使用state的形式。
2 初始化state
下面展示一个小案例,通过点击文本,来切换今天的天气,如下图2-1所示:
第一步先把文本通过React组件显示在页面上,代码2-1如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件对象三大属性之一state</title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 1.类式组件
class Weather extends React.Component {
constructor(props) {
super(props)
// 初始化状态
this.state = {isHot: false}
}
render() {
// 读取状态
console.log(this);
return <h2>今天天气很{this.state.isHot? '炎热': '凉爽'}</h2>
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
</body>
</html>
state初始化及访问步骤:
- 在构造函数中this.state=值(对象)
- 在render方法中{}内this.state.key访问key对应的值
注:
- state是Weather实例中的属性,要想初始化需要借助构造器。
- render中的this就是当前组件的实例对象
问题:
- Weather实例不是由我们new,而是React创建的,React在创建时,构造函数传递了那些参数呢?
- 构造器中的super能不能省略呢?
借助开发者工具查看下组件实例,如下图2-1所示:
3 React中事件绑定
在上面呢,我们完成了state的初始化和读取,实现了文本的展示,那么如果通过鼠标点击来完成切换呢?
参考js很容易想到,要通过绑定鼠标点击事件来完成。那么在Reac中如何绑定事件呢?
第一步在类中定义鼠标点击处理函数:
changeWeather() {
console.log(this);
this.state.isHot = !this.state.isHot
}
第二步在render函数返回标签上绑定点击事件:
render() {
// 读取状态
console.log(this);
return <h2 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
}
注:
- onClick遵循小驼峰命名规则
- this.changeWeather后不加()
鼠标点击查看console输出:
undefined
Inline Babel script:25 Uncaught TypeError: Cannot read properties of undefined (reading 'state')
为啥呢?下面讲解
4 类方法中this指向问题
示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3_类方法中this的指向</title>
</head>
<body>
<script type="text/javascript">
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
speak() {
// speak方法放在Person类原型对象上,供实例使用
// 通过Person实例调用本方法,方法中的this指向Person实例
// 类中普通方法默认开启严格模式
console.log(this);
}
}
const p1 = new Person('zhangsan', 25)
// 通过实例调用类方法
p1.speak()
// 直接调用方法,因为默认开启了严格模式,this为undefied
const f1 = p1.speak
f1()
function demo1() {
console.log(this);
}
function demo2() {
'use strict'
console.log(this);
}
demo1()
demo2()
</script>
</body>
</html>
输出:
Person
undefined
Window
undefined
5 解决React类组件方法this指向
解答下上面为什么点击,调用changeWeather(),输出是undefied问题?
onClick={this.changeWeather},这里为鼠标点击之前,通过this实例原型链找到changeWeather方法,交给onClick用于回调。当点击的时候属于直接调用changeWeather()方法,而不是通过Weather的实例对象。
又因为类中普通方法默认开启严格模式,且经过babel翻译,所以是undefined不是windows也不是Weather实例
解决方案一:既然此处是非实例调用,导致this执行改变,那么我们通过bind方法改变this指向为Weather实例并重新放置在Weather实例上。
代码调整如下:
<script type="text/babel">
// 1.类式组件
class Weather extends React.Component {
constructor(props) {
super(props)
// 初始化状态
this.state = {isHot: false}
this.changeWeather = this.changeWeather.bind(this)
}
render() {
// 读取状态
console.log(this);
return <h2 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
}
changeWeather() {
// changeWeather放在了哪里?Weather的原型对象上,供实例使用
// 由于changeWeather上作为onClick的回调,所以不是通过实例调用的,上直接调用
// 类中的方法默认开启了严格模式,所以changeWeather中的this为underfided
// console.log(this);
let isHot = this.state.isHot
this.state.isHot = !isHot
console.log(this.state.isHot);
}
}
// 2.渲染虚拟DOM到页面
ReactDOM.render(<Weather/>, document.getElementById('test'))
解决方案二:我们把一个匿名函数交给onClick回调,匿名函数内部执行this.changeWeather()方法,此时this就是Weather实例。
调整代码如下:
class Weather extends React.Component {
constructor(props) {
super(props)
// 初始化状态
this.state = {isHot: false}
}
render() {
// 读取状态
console.log(this);
// 匿名函数
return <h2 onClick={() => this.changeWeather()}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
}
changeWeather() {
// changeWeather放在了哪里?Weather的原型对象上,供实例使用
// 由于changeWeather上作为onClick的回调,所以不是通过实例调用的,上直接调用
// 类中的方法默认开启了严格模式,所以changeWeather中的this为underfided
// console.log(this);
let isHot = this.state.isHot
this.state.isHot = !isHot
console.log(this.state.isHot);
}
}
其他代码同上
不管是第一种bind改变this执行,还是事件监听回调绑定匿名函数都可以把上面的问题解决,但是鼠标点击,页面并没有和预想的一种,点击切换天气,这又是啥问题呢?明明this.state.isHot的值在控制台打印是改变了的。下面5setState部分给出解答。
5 setState
5.1 更新state状态数据方式
react中state中状态数据,不可直接更改,需要借助内置的API(setState)更改。
继续调整changeWeather()方法中更改数据的方式,如下:
changeWeather() {
// 状态需要通过setState修改, 且更新是一种合并,不是替换
this.setState({isHot: !this.state.isHot})
}
该setSate()放置在React.Component原型对象上,可以直接通过this调用。
5.2 更新state整体替换还是合并相同项?
那么this.state的值是一个对象,我们单独更新其中一个key-value,结果是整体替换还是合并原有的this.state值呢?
测试,在this.state初始化中在加一个key-value,如下
constructor(props) {
super(props)
// 初始化状态
this.state = {isHot: false, wind: '微风'}
}
如下图所示:
React的state中的状态数据更新是合并更新,相同key替换对应的值,不同保持原样。
现象:
- 开发者工具中value值更新并不实时,有一定延迟,不知道啥原因。
5.3 关于构造器、render方法调用次数
- 构造器:组件实例化时才执行,即页面有几个该类型组件实例,调用几次
- render方法:1+n次,1是初始化时调用,n为状态更新次数
6 state简写
在日常开发中,我们怎么使用state呢?
需求说明:
- 我们只是想初始化转态
- 确保自定义方法中this指向组件实例
解决:
- 把state在类中方法外初始化
- 使用箭头函数
知识点:
-
实例变量
-
定义:实例变量是类中方法外的变量,不过没有static修饰,也叫对象变量(new出来的);或者构造方法this.XXX
-
作用有效范围:实例变量在对象创建的时候创建,在对象被销毁的时候销毁;
-
默认值:实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定;
-
-
箭头函数:箭头函数内部this为其外层所在区域的this。如果在类中为类的实例对象;否则为全局对象window。
简化代码如下,其他同上:
// 1.类式组件
class Weather extends React.Component {
// 初始化state
state = {isHot: false, wind: '微风'}
render() {
return <h2 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'},{this.state.wind}</h2>
}
// =赋值表示changeWeather是实例方法
changeWeather = () => {
this.setState({isHot: !this.state.isHot})
}
}
7 问题解答
-
2.1-暂时给出官网参考,参考官网首页,如下图7-1所示:
- props是什么?下个篇章讲解props
-
2.2 类的语法规定:如果B类继承A类,而A类中写了构造器,那么B类构造器中super必须要调用。
后记
❓QQ:806797785
⭐️源代码仓库地址:https://gitee.com/gaogzhen/react-study
参考:
[1]尚硅谷React教程(2022加更,B站超火react教程)[CP/OL].2020-12-15.p7-p19.
[2]JS中Class类的静态属性和静态方法[CP/OL].