React 学习(三) ---- state 和 事件处理函数
在上两节中,我们讲述了props, 组件使用props进行渲染,但是这是一次性的, props渲染完成之后就不做任何事情了,但是现实中却不是这样的,当我们点击购物车上的加减按钮时,数量会自动加1或减1,还有在就是倒计时效果,这就用了组件内部的状态, 如果一个组件有内部状态,那只能用类式组件,因为类中拥有构造函数,构造函数表示就是一个对象实例上的属性,对象实例上的属性都是特有的,所以也可以称之为对象实例的状态。
在组件的构造函数中声明组件的状态也是很简单,直接写this.state = {},我们在这个对象中添加组件中使用到的状态。如我们写一个加减组件,它需要一个状态保存数字,我们可以写 this.state= {count: 0},然后我们在render函数中就可以this.state.count获取组件中的状态进行渲染。现在我们写一个counter 组件。
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
render() {
const buttonStyle = {
margin: '10px'
};
return (
<div>
<button style={buttonStyle}>+</button>
<button style={buttonStyle}>-</button>
<span>count: {this.state.count}</span>
</div>
);
}
}
你可以注意到,调用构造函数时, 多了super(props) 语句,这是es6 的语法规定, 子类中没有this, 只能调用super生成子类的 this,如果在调用super 之前使用this, 就会报错。 这个地方其实是不会变化的,可以看成一个模式。每次给组件添加状态的时候,我们就按照这个模式书写就可以了。先写
constructor(props) {
super(props);
}
要添加什么状态,直接在构造函数里面super下面写this.state = …. 就可以了。
constructor(props) {
super(props);
this.state = {
count: 0
}
}
页面效果如下,组件中获取到了state中定义的状态。

constructor 构造函数中的props也是指父组件传递过来的props, 这里接受父组件传递过来的props的主要作用是可以使用props对状态进行初始化。比如, this.state = {count : props.initCount} , 这时count就初始化为父组件传递过来的值,而不是0.
其实constructor 还有一个默认参数 context, 上下文,React的context API, 这个以后再说。
constructor(props, context) {
super(props, context)
this.state = {}
}
如果我们的构造函数中,这两个参数都用不到,我们完全可以写空参数的构造函数
constructor() {
super();
this.state = {
count: 0
}
}
构造函数说完了,我们也给组件添加了状态,那怎么才能更改状态呢,当点击加号的时候,count 加1, 点击减号, count 减1? 首先要给加减button 添加click事件,然后再在事件中进行加减1的操作,状态的更改
React 中给元素添加事件,就像我们给DOM元素添加行内事件一样简单,直接在元素身上写事件属性就可以了。但这里也有两点不同,事件名要用驼峰命名法,如click事件要写成onClick,事件处理函数是一个函数,用{} 括起来, <button onClick={handleClick}>点击</button>。那么这又引出了另外一个问题,handleClick 函数写在什么地方? 这里用到了ES6中类的语法,我们在一个类中写函数,该函数会自动绑定到类的原型上,对象实例通过 this 就能获取到。相对应地,在组件类中,我们直接写事件处理函数,然后在render函数中通过this 获取。现在我们给button 添加click 事件
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
// 加号事件处理函数
handleClickIncrement() {
console.log('加1')
}
// 减号事件处理函数
handleClickDecrement() {
console.log('减1')
}
render() {
const buttonStyle = {
margin: '10px'
};
return (
<div>
{/*button 添加事件处理函数*/}
<button style={buttonStyle} onClick={this.handleClickIncrement}>+</button>
<button style={buttonStyle} onClick={this.handleClickDecrement}>-</button>
<span>count: {this.state.count}</span>
</div>
);
}
}
现在点击加减按钮,控制台上可以看到加1或减1出现,表明我们绑定事件成功。现在就要改变状态了,对于状态的改变,React不允许直接更改状态, 或者说,我们不能给状态(如: count)进行赋值操作, 为此react 提供了一个 setState函数,必须调用它来更改状态。它接受一个对象或一个函数作为参数,当接受一个对象时,对象的键就是要改变的状态,对象的值就是状态改变之后的值。当接受一个函数时,react 自动注入两个参数,一个是prevState, 一个props,然后返回一个对象,返回的对象和它接受的对象是一致的,键是要改变的状态,值就是状态更改后的值。对于prevState, 它指的就是组件的上一次状态,构造函数中this.state对象。Props,很好理解,就是这个组件接受的属性。我们在handleClickIncrement 和handleClickDecrement 事件处理函数中调用this.setState方法来更改状态。
// 加号事件处理函数
handleClickIncrement() {
this.setState({
count: this.state.count + 1
})
}
// 减号事件处理函数
handleClickDecrement() {
this.setState({
count: this.state.count - 1
})
}
这时点击加或减,控制台竟然报错了,

我们只使用了this.setState, 难道 this 是undefined, 我们在handleClickIncrement 和handleClickDecrement 事件处理函数中console.log(this), 发现它确定是undefined
// 加号事件处理函数
handleClickIncrement() {
console.log(this);
this.setState({
count: this.state.count + 1
})
}
也就是this 并没有指向React 组件实例,看来我们要修正this的指向问题,使它指向我们这个组件实例。改变this指向有以下几种方法:
一种是ES5提供的bind()方法,它的第一个参数就是指定函数中this的,且它返回 一个函数,可以知道,返回的这个函数中this已经写死了,在程序运行的时候也不会变化了。我们在什么位置上使用bind方法? 在构造函数中,因为构造函数中的this, 就是具体创建的对象实例,在构造函数中把this 传递给bind作为第一个参数,bind 返回的参数中的this 就固定成了对象实例。构造函数中修改如下,现在点击加减没有问题了。
constructor(props) {
super(props);
this.state = {
count: 0
}
// bind方法绑定this
this.handleClickIncrement = this.handleClickIncrement.bind(this);
this.handleClickDecrement = this.handleClickDecrement.bind(this);
}
另一种是箭头函数,因为箭头函数里面的this继承于包围它的函数,那么我们直接把click 事件处理函数写成箭头函数,里面的this 就指向我们这个组件。
// 事件处理函数赋值一个箭头函数
handleClickIncrement = () => {
this.setState({
count: this.state.count + 1
})
}
handleClickDecrement = () => {
this.setState({
count: this.state.count - 1
})
}
注意这种写法还没有被浏览器支持,React 库是默认支持的,所以在这里是没有问题的。如果你以后使用wepback等构建工具时,要自己进行配置。
其实箭头函数还有一种写法,就是我们在绑定事件处理函数的时候,不直接写this. 而是写一个箭头函数,
<button style={buttonStyle} onClick={() => this.handleClickIncrement()}>+</button>
<button style={buttonStyle} onClick={() => this.handleClickDecrement()}>-</button>
这种方法不太常见,因为写起来比较麻烦,但是它有一个好处,因它是一个函数,我们可能把实例上的属性传递给事件处理函数。
刚才提到过setState还可以接受一个函数,React 为它注入prevState 参数和props 参数,我们这里,只用到prevState 参数。把加号事件处理函数用函数写一下。
// 加号事件处理函数
handleClickIncrement() {
this.setState(prevState => {
console.log(prevState);
return {
count: prevState.count + 1
}
})
}
我顺便打印了一下prevState, 可以看到,页面上显示6,而prevState是5, 它就是更改之前的状态,我们只有获取到更改之前的状态,才能修改它,进而形成新的状态。

这里对事件处理函数做一下解释,虽然它的语法看着像行内事件,但它是用了事件委托的方式来处理事件。无论有多少个事件出现,其实最后都只在DOM树上添加一个事件处理函数,挂在最顶层的DOM节点上。所有的事件都被这个事件处理函数捕获,然后根据具体组件分配到特定函数,使用事件委托的性能当然比为每一个元素添加事件性能更好。因为React 控制了组件的生命周期,在unmount的时候,自然能够清除相关的所有事件处理函数,从而不会造成内存泄露。
React 学习(三) ---- state 和 事件处理函数的更多相关文章
- React学习之State
本文基于React v16.4.1 初学react,有理解不对的地方,欢迎批评指正^_^ 一.定义组件的两种方式 1.函数定义组件 function Welcome(props) { return & ...
- react学习(三)之生命周期/refs/受控组件 篇
挂载/卸载 //在类组件中 class Clock extends React.Component { constructor(props) { super(props); this.state = ...
- react中 props,state与render函数的关系
我们很明显的能够感受到,react是一门数据驱动的框架,当数据发生变化,页面就会自动发生变化,他背后的原理是怎么样子的呢 比如todolist例子里面,inputValue变了,框里面的内容就会自动变 ...
- react学习三
三点运算符 (...)的用法 1:展开运算符 let a=[1,2,3]; let b=[0,...a,4];//[0,1,2,3,4] let obj ={a:1,b:2}; let obj2 = ...
- python学习 (三十) python的函数
1: 函数参数默认值 def method1(p1 = , p2 = ): // 函数有两个参数,并且都有默认值 return p1 + p2 print(method1()) print(meth ...
- python学习三十五天函数递归的用法
python函数递归就是自己调用自己,无限循环,但是python限制了调用的次数1000次,就会终止,递归用在栏目分类,采集程序比较多,下面简单说函数递归用法和实例 1,函数递归用法 def func ...
- python学习三十四天函数高阶函数定义及用法
python函数高阶函数是把函数当成一个变量,传递给函数作为参数,或者函数的返回值里面有函数,都称为高阶函数, 1,把函数作为参数传递 def dac(x,y): return x+y def tes ...
- 为什么React事件处理函数必须使用Function.bind()绑定this?
最近在React官网学习Handling Events这一章时,有一处不是很明白.代码如下: class Toggle extends React.Component { constructor(pr ...
- React学习笔记(六)事件处理
React学习笔记(六) 五.事件处理 React事件绑定属性的命名采用驼峰写法,不同于传统DOM全部小写. 如果采用JSX的语法,事件函数需要用大括号{}包裹函数名,不同于传统DOM字符串小括号的方 ...
随机推荐
- RabbitMQ详解(二)------消息通信的概念
PS:近期在南宁出差,工作比较忙,所以更新会比较慢. 说到消息通信,可能我们首先会想到的是邮箱,QQ,微信,短信等等这些通信方式,这些通信方式都有发送者,接收者,还有一个中间存储离线消息的容器.但是这 ...
- [转]关于oracle sql语句查询时表名和字段名要加双引号的问题
oracle初学者一般会遇到这个问题. 用navicat可视化创建了表,可是就是不能查到! 后来发现②语句可以查询到 ①select * from user; 但是,我们如果给user加上双引 ...
- .NET Core中复制源文件夹下的所有内容到新文件夹
.NET Core中没有原生的复制文件夹方法,我们可以自己写个: 新建一个.NET Core控制台项目,示例代码如下: using System; using System.IO; namespace ...
- HTML+CSS之盒子模型
一.元素分类 CSS中html的标签元素大体分为三种类型 1.块状元素 @特点: #每个块级元素都从新的一行开始,并且其后的元素也另起一行(一个块级元素独占一行) #元素的高度.宽度.行高以及顶和底边 ...
- A2D JS框架 - Web API CSRF保护实现
这次自己实现了类似jQuery中ajax调用的方法,并且针对RESTFul进行了改造和集成,实现的A2D AJAX接口如下: $.ajax.RESTFulGetCollection("/ap ...
- 六、Xadmin忘记密码
1.如果用的是django自带的User模块,忘记了超级用户的密码,可以通过以下方法找回密码: 终端进入项目根目录,然后输入如下命令: python manage.py shell 然后在python ...
- H5 CSS的格式
02-CSS的格式 标签名称{ 属性名称: 属性对应的值; ... } 2.注意点: 1.style标签必须写在head标签的开始标签和结束标签之间(也就是必须和title标签是兄弟关系) 2.sty ...
- 行政区划sql数据脚本
行政区划sql数据脚本 IF (EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TB_Province]') ...
- iOS上手指点击波纹效果的实现
https://www.jianshu.com/p/35e6f53ca0fe 2016.10.19 22:00* 字数 135 阅读 2468评论 2喜欢 7 闲暇时间做了一个反馈手指点击屏幕的效果, ...
- 软件工程(FZU2015) 赛季得分榜,第10回合(alpha冲刺)
SE_FZU目录:1 2 3 4 5 6 7 8 9 10 11 12 13 积分规则 积分制: 作业为10分制,练习为3分制:alpha30分: 团队项目分=团队得分+个人贡献分 个人贡献分: 个人 ...