壹 ❀ 引

从零开始的react入门教程(二),从react组件说到props/state的联系与区别一文中,我们介绍了react组件的基本用法以及props与state的区别。其中react组件分为函数组件class组件,函数组件一般比较简单,它的函数名首字母需要大写,接受外部传入的props并返回react元素,这是一个简单的函数组件。

function App(props) {
return <div>{props.name}</div>
}

除此之外就是功能性更强的Class组件,如下是一个简单的react Class组件。

class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '听风是风'
}
}
componentDidMount() {
console.log('dom渲染完成');
}
render() {
console.log('此时开始渲染dom');
return <h1>我是{this.state.name}</h1>
}
}

在上述例子中,我们在constructor中初始化了State并继承父组件传入的props(props与state将共同构成组件App的数据),在render方法中我们定义了组件App需要渲染的dom结构,而在componentDidMount方法中我们可以做一些渲染完成后的操作,比如发起接口请求。这三个方法都属于react生命周期钩子函数,关于更详细的生命周期介绍,我会在后续单独拿一篇文章展开介绍。

除了介绍组件外,我们还介绍了react中非常重要的概念propsState,react是单向数据流,对于一个组件而言,它能接收外部传递的数据props,需要注意的是props仅限只读,不可修改,如果要修改可以在父级专门提供修改数据的方法一并传递过来,或者子组件拷贝一份自己玩。而State则是组件内部定义的私有属性,react提供了专门的方法用于修改State,同样State也能作为props传递给自己的子组件,props与State一同构成了react的数据流。

贰 ❀ 奇怪的事件绑定

在上篇文章中,我们定义了一个简单的让数字自增的组件,但在方法绑定上至少是让我非常难受,比如下面这个例子:

// 这是一个子组件,它会调用父组件传递过来的方法
class Son extends React.Component {
render() {
return <button onClick={() => this.props.click()} >click me</button>
} }
// 这是一个父组件,它会传递一个方法给子组件调用
class Parent extends React.Component {
state = {
name: '听风是风'
}
handleClick() {
console.log(this.state.name);
}
render() {
return <Son click={() => this.handleClick()} />
}
} ReactDOM.render(<Parent />, document.getElementById('root'));

不知道大家有没有对于上述例子中的事件绑定/事件传递感到别扭,按我们印象中理解事件绑定如vue或者小程序,大多数应该是如下这样,但react硬是写成了一个箭头函数,方法后还带了圆括号,非常反人类:

<button onClick={事件名} >click me</button>

如果我们将上面的例子的事件传递去除掉外层的箭头函数以及圆括号,点击按钮就会报错:

<Son click={this.handleClick} />

但如果我们保留父组件箭头函数式的事件传递,只是将子组件调用处改为如下这样,你会发现又能正常执行:

<Son click={() => this.handleClick()} />
<button onClick={this.props.click} >click me</button>

这说明问题就出在事件传递上,而不是使用上。我们再看个奇怪的例子:

// 同样是调用父组件方法的子组件
class Son extends React.Component {
render() {
// 注意,这里的方法调用方法被修改了
return <button onClick={this.props.click} >click me</button>
} }
// 同样是传递方法给子组件使用的父组件
class Parent extends React.Component {
// 注意,这里方法没用state的数据。
handleClick() {
console.log(1);
}
render() {
// 注意,这里方法传递被修改了
return <Son click={this.handleClick} />
}
}

很神奇,当父组件传递过去的方法没用state数据,我们在方法传递与使用上似乎都可以按照传统的写法了。不卖关子,其实当我们修改上面例子事件传递出错时,错误已经说的很清楚,访问不到name,本质上就是因为this丢失了,this都没了,自然访问不到state数据或者props,这才出了错。而前文click={() => this.handleClick()这种写法的目的就是为了将this与方法绑定在一起。

而第二个例子中,传递方法内部并没有与state数据或props有联系,你只要把方法给我调用就好了,所以才可以按照我们理解的去改写后还能正常执行。

那么问题就来了,动动小脑瓜子想想项目开发中大部分方法肯定会与state挂钩,难道我们在方法传递上难道就一定要忍受这份屈辱吗?其实并不是,对于事件的this绑定上react提供了好几种方式,我们接着说。

叁 ❀ react事件绑定

叁 ❀ 事件命名

与传统事件命名习惯一样,react对于事件命名同样推荐小驼峰,比如getUserNameverifyEmail,在了解这些之后,让我们从最简单的点击事件说起。

传统JS实现点击依赖的是onclick事件,比如:

<button onclick="eventName()">
click me
</button>

其中onclick响应用户点击行为,点击之后会触发eventName中的的代码逻辑。同理,在react中也封装了类似的交互事件,比如点击事件在react中为onClick,监听表单输入变化的事件onChange等等;react在事件监听名与事件响应方法命名上官方也有推荐,一般为on[Event]handle[event],我们看个例子:

class Parent extends React.Component {
state = {
num: 1,
}
handleClick() {
this.setState(state => ({
num: ++state.num
}));
} render() {
return (
<div className="demo">
<div>{this.state.num}</div>
<button onClick={() => this.handleClick()} >click me</button>
</div>
)
}
}

(注意,由于class在react中属于关键字,所以得使用className,经过react封装效果与class一样)

但一个组件中可能存在多个点击响应方法,大家都叫handle[event]就难以区分了,所以开发中可以用handle[语义化方法名]来命名我们的响应函数,比如handleGetUserName

这里我们只是举例说明了事件其一的onClick,类似经过react封装的合成事件还有很多,比如复制粘贴onCopy、onCut、 onPaste,滚动事件onScroll等等,若对于是否有这个事件可用的疑问时,可以扫一遍官方事件列举表react合成事件,用法这里就不一一介绍了。

叁 ❀ 贰 事件的this绑定

我们在上文中描述了react对于事件绑定的奇怪做法,这么做的原因是由于在JavaScript中,class默认不会帮你绑定this,如果我们不做绑定,在事件函数传递时会导致this丢失,从而为undefined。

react一共提供了三种为函数绑定this的做法,第一种我们已经在上文中有举例了,第二种做法是在组件constructor中利用bind直接绑定this,比如:

class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
num: 1
}
// 在constructor中为方法绑定this
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
num: ++state.num
}));
} render() {
return (
<div className="demo">
<div>{this.state.num}</div>
{/* 注意,这里就不用箭头函数返回 */}
<button onClick={this.handleClick} >click me</button>
</div>
)
}
}

第三种做法其实与第一种做法类似,只是箭头函数不是写在事件绑定后,而是在方法声明时就已经使用了箭头函数,比如:

class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
num: 1
}
}
// 声明时使用箭头函数
handleClick = () => {
this.setState(state => ({
num: ++state.num
}));
} render() {
return (
<div className="demo">
<div>{this.state.num}</div>
{/* 注意,这里就不用箭头函数返回 */}
<button onClick={this.handleClick} >click me</button>
</div>
)
}
}

那么关于事件的this绑定就说到这。

叁 ❀ 叁 事件中的e对象

在事件处理时,我们往往会有需要知道点击的是哪一个元素的场景,比如我们通过一段模板遍历生成了多个元素,它们都绑定的为同一个事件响应函数,我们希望点击这个元素时获取它的专属class名怎么做到呢?与angularjs或vue一样,react同样提供了e对象,比如:

class Parent extends React.Component {
handleClick = (e) => {
console.dir(e.target)
} render() {
return (
<div className="demo">
<button onClick={(e) =>{this.handleClick(e)}} >click me</button>
</div>
)
}
}

点击按钮,我们在控制台打印了这个dom对象,就像js一样,我们可以访问到很多我们熟悉的属性,比如下面的例子,我们点击元素获取当前元素的class名:

class Parent extends React.Component {
state = {
name: ''
}
handleClick(e) {
this.setState({ name: e.target.className });
} render() {
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((numbers, index) =>
<li className={'li-' + numbers} onClick={(e) => { this.handleClick(e) }} key={index}>{numbers}</li>
);
return (
<div className="demo">
<ul>
{listItems}
</ul>
<div>当前点击的li的class名为:{this.state.name}</div>
</div>
)
}
}

除此之外,我们可能还会有阻止元素默认行为或者阻止事件冒泡等类似的需求,e对象也能帮我们做到这一点,比如e.preventDefault()

传递e对象也有两种行为,上述例子中我们提供的是显示,很直接的将e作为参数传递了进度,第二种是通过bind隐式传递,它们效果是一致的,像这样:

handleClick = (id, e) => {
// do something...
}
// 显示,e直接卸载参数上
<button onClick = { (e)=> this.handleClick( id, e ) }></button>
// 隐式,通过bind绑定,但还是可以在方法中通过e获取
button onClick = { this.handleClick.bind( this, id ) }></button>

另外,当我们同时需要获取event以及传递自定义参数时,还支持函数柯里化的写法,下面写法等同于上面写法:

handleClick = ( id ) => {
return (event) => {
console.log( event, id )
}
} //函数合理化写法
button onClick = { this.handleClick( id ) }></button>

肆 ❀ 总

为什么这么久没更新呢,仔细想想理由,应该是懒散 > 工作饱和人比较疲惫 > 家里的电脑坏了懒得用公司笔记本,为了恢复博客更新频率,现在已经在强迫自己下班后留在公司花2小时左右时间学习与写博客了,所以不出意外(一定)这个月会恢复以往频率,毕竟还是还是得努力学习才行。

这篇文章只是简单介绍了react事件的用法以及部分注意项,由于react合成事件还是挺多,一个个说篇幅过长,大部分事件用法与我们以往所用框架相同,所以我的观点是需要用某个事件了,去扫一遍官网提供的事件,用几次自然就熟悉了。

另外由于react官方文档说的比较简短,为了避免部分知识点官网介绍不全,我也在读相关react书籍,尽可能对于一个知识点多搜集一些信息,下篇文章应该是周末更新,那么本文就先写到这里了。十点了,下班去坐地铁!!!

从零开始的react入门教程(三),了解react事件与使用注意项的更多相关文章

  1. react 入门教程 阮一峰老师真的是榜样

    -  转自阮一峰老师博客 React 入门实例教程   作者: 阮一峰 日期: 2015年3月31日 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Nati ...

  2. React入门教程(二)

    前言 距离上次我写 React 入门教程已经快2个月了,年头年尾总是比较忙哈,在React 入门教程(一)我大概介绍了 React 的使用和一些注意事项,这次让我们来继续学习 React 一. Rea ...

  3. React入门教程1---初见面

    React入门教程1---初见面:https://blog.csdn.net/solar_lan/article/details/82799248 React 教程 React 是一个用于构建用户界面 ...

  4. 无废话ExtJs 入门教程三[窗体:Window组件]

    无废话ExtJs 入门教程三[窗体:Window组件] extjs技术交流,欢迎加群(201926085) 1.代码如下: 1 <!DOCTYPE html PUBLIC "-//W3 ...

  5. PySide——Python图形化界面入门教程(三)

    PySide——Python图形化界面入门教程(三) ——使用内建新号和槽 ——Using Built-In Signals and Slots 上一个教程中,我们学习了如何创建和建立交互widget ...

  6. Elasticsearch入门教程(三):Elasticsearch索引&映射

    原文:Elasticsearch入门教程(三):Elasticsearch索引&映射 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文 ...

  7. RabbitMQ入门教程(三):Hello World

    原文:RabbitMQ入门教程(三):Hello World 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...

  8. JasperReports入门教程(三):Paramters,Fields和Detail基本组件介绍

    JasperReports入门教程(三):Paramter,Field和Detail基本组件介绍 前言 前两篇博客带领大家进行了入门,做出了第一个例子.也解决了中文打印的问题.大家跟着例子也做出了de ...

  9. 25、react入门教程

    0. React介绍 0.1 什么是React? React(有时称为React.js 或ReactJS)是一个为数据提供渲染HTML视图的开源JavaScript库. 它由FaceBook.Inst ...

  10. React入门教程

    做前端的人都知道,目前热门前端的框架是 VAR => Vue,Anglur,React. 而如果说最热门的前端框架是谁,毫无悬念是 React React 是由 Facebook 主导开发的一个 ...

随机推荐

  1. 斐波拉契序列的 Go 实现

    本篇文章主要介绍斐波拉契序列的 Go 语言实现. 斐波拉契序列: 前面相邻两项之后构成后一项. 1. 循环迭代 package main import "fmt" const ma ...

  2. springBoot 整合 hikari

    Hikari是一款非常强大,高效,并且号称"史上最快连接池".并且在springboot2.0之后,采用的默认数据库连接池就是Hikari.不需要引入依赖,已经在SpringBoo ...

  3. spring cloud 通过feign请求设置请求头

    本文为博主原创,转载请注明出处: spring cloud 服务组件之间通过feign 的方式请求,会携带很少的基础类型的消息头参数,比如Content-Type等,但不会携带自定义或指定的请求头参数 ...

  4. 结合SK和ChatGLM3B+whisper+Avalonia实现语音切换城市

    结合SK和ChatGLM3B+whisper+Avalonia实现语音切换城市 先创建一个Avalonia的MVVM项目模板,项目名称GisApp 项目创建完成以后添加以下nuget依赖 <Pa ...

  5. Redis-有序集合-zset

  6. [转帖]在Linux中切换cmake版本

    在Linux中切换cmake版本https://blog.whsir.com/post-6804.html   在Linux系统中,有时需要使用cmake进行程序编译,由于不同的Linux系统导致安装 ...

  7. [转帖]Nginx - 根据IP分配不同的访问后端

    https://www.cnblogs.com/hukey/p/11868017.html 1. 需求分析 为了在线上环境提供测试分支,规定将某IP转发到测试程序地址.如果是 ngx 直接对外,采用 ...

  8. [转帖]Oracle的审计

    AUDIT_TRAIL 初始化参数AUDIT_TRAIL用于控制数据库审计,默认值为none. 参数类型: String 默认值: none 允许动态修改: 否 基本参数: 否 语法: AUDIT_T ...

  9. Harbor的逻辑备份与学习

    Harbor的逻辑备份与学习 背景 一直想处理一下一个有网络冲突的Harbor镜像服务器 但是因为网络层自己水平一直是不是非常自信 加上Harbor容器使用的compose的玩法, 自己不敢直接处理. ...

  10. [转帖]网络传输性能netperf测试方法和下载

    简介 Netperf是一种网络性能的测试工具,主要针对基于TCP或UDP的传输.Netperf根据应用的不同,可以进行不同模式的网络性能测试,即批量数据传输(bulk data transfer)模式 ...