上节课我们学习了如何初始化state,而且也介绍了如何在我们的组件中来调用state,在上节课的Demo中呢,我们已经完全可以做到用state来展示在页面上,但是我们却依然还是只能展示了这个静态页面,我们想要的点击事件却依然还是没能成功实现,当时是哇了一个坑,那么这节课我们来填坑了。

回顾

按照老规矩啊,在填坑之前我们肯定还是要对上节课的内容来做一个简单的回顾。

  • 如果通过state来驱动页面需要借助构造器方法
  • 构造器方法的形参官方给定的是props
  • 构造器方法要在第一步将props传给super
  • state的类型十一个对象
  • 通过this可以来调用到state

添加点击事件

我们回想一下,上节课我们已经成功地通过state来驱动页面展示,但是我们那个点击文字完成状态切换还没有实现。

如果要实现这个点击切换状态的功能,我们的最起码的前提是什么呢?

那我肯定得让这段文字支持点击操作啊,我这段文字连点击操作都不支持还怎么往下进行啊?

那么第一步,我们就给这段文字添加上点击事件

原生js事件绑定

在学习react事件绑定之前,我们来回忆一下,原生js做事件绑定都有那些方法呢?

例如我现在页面上有三个按钮,分别叫按钮1按钮2按钮3,我现在要求这三个按钮点击的时候都会有一个弹窗展示。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>bind</title>
</head>
<body>
  <button id="btn1">按钮1</button>
  <button id="btn2">按钮2</button>
  <button>按钮3</button>

  <script type="text/javascript">

  </script>
</body>
</html>

这个我们该怎么去实现呢?第一种方式我们先用最繁琐的一个方式来处理一下

const btn1 = document.getElementById("btn1");
btn1.addEventListener("click", () => {
    alert("按钮1被点击了");
});

我们来看一下效果怎么样:

image-20211216142126106

不难看出这种方法是可以很成功地实现我们的需求,但是我们也知道其实还有一个我们很熟悉的方式来添加点击事件的。没错,就是onclick,那么我们就用这个方式来写按钮2吧。

const btn2 = document.getElementById("btn2");
btn2.onclick = () =>{
    alert("按钮2被点击了");
}

我们来看一下这方法的效果:

image-20211216142434653

很显然,这个方法也是没有毛病的,但是还有没有其他的方式呢?我们的按钮3要怎么写呢?

<button onclick="notify()">按钮3</button>
<script type="text/script">
  function notify() {
    alert("按钮3被点击了");
  }
</script>

我们直接给按钮3添加了一个onclick属性,至于是什么意思呢?就是我们点击了按钮3,程序就会去调用onclick属性中的函数。但是用这个方式的话就需要我们在js中定义相关的函数了。我么莱卡一下具体效果。

image-20211216143112866

也是很正常的展示了我们预期的结果。

以上便是原生js来添加点击事件的方法。

react添加点击事件

我们通过上文了解了通过原生js添加点击事件,那么在react中我们的点击事件又该怎么添加呢?

其实理论上来讲,js中的那 3 种方法都可以用的。但是我们还记不记得react的一个特点啊?操作虚拟DOM来代替操作真实DOM

而我们之前按钮1按钮2使用的方法那不就是在操作真实DOM嘛。这就很不符合react的设计哲学了。那我们可想而知,在react中那是大理推崇按钮3的方法的。

那么我们就来试试呗:

class Weather extends React.Component {
    constructor(props) {
        super(props);
        this.state = { isHot: false }
    }

    render() {
        return <h2 onclick="notify()">今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h2>
    }
}
ReactDOM.render(<Weather/>, document.getElementById("test"));

function notify() {
    console.log("标题被点击了!");
}

看我们上面那一段代码。前面的构造器方法我们就不赘述了,看render方法里面,<h2>标签我们给添加了onclick属性,onclick属性执行的是notify函数,然后下面我们定义了notify函数。

当然这次我们就不弹窗了,就在控制台打印信息吧,方便我们测试多次点击。

来看一下效果:

image-20211216145142335

报错了,这你说想不到的事情,但是我们看看报错信息怎么说的:不允许使用onclick属性,你想要用的是不是onClick属性?注意这是大写的C

当然了我们这里也顺带提一句,react还干了一件事儿,就是所以htmljs里的on***监听属性都给重写了一遍,所有的这一类属性on后面的首字母必须大写,比如onclick要写成onClick,onblur要写成onBlur。这是硬性规定。当然在以后的内容我们也会再深入一点去探讨为什么,现在我们先记住,埋个伏笔在这。

那么好这种事情我们之前是不是也遇到过类似的,既然官方提醒我们是不是要用onClick那我们就改过去试一试呗。

image-20211216150104266

我们也按要求改了,但是诶,还是不对,告诉我们onClick属性呢必须是一个函数,但是我们获取到的却是string类型。那么好吧我们再改一下

class Weather extends React.Component {
  ...
    render() {
        return <h2 onClick={notify()}>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h2>
    }
}
ReactDOM.render(<Weather/>, document.getElementById("test"));

function notify() {
    alert("标题被点击了!");
}

那我这次总行了吧?而且我还是很规矩地用{}notify函数给括起来了。那么来看:

或许大家发现我上面代码把console.log改成了alert,为了方便观察,因为下面的现象有点意思

image-20211216151507847

因为这一次页面还没来得及渲染成功就已经弹窗了。也就是说在渲染页面之前就调用了notify函数。

这是为什么呢?

我们来回顾一下react渲染组件的流程:

  1. react解析组件标签
  2. 发现组件是类定义的,随后实例化类
  3. 通过类组件的实例对象调用render方法

到这就够了,调用了render方法,render方法要return虚拟DOM出来啊,也就是return关键字后面的jsx代码就会直接被执行,然后把结果return出来。所以说这里代码执行了,但是页面还没被渲染。

可能有人说,这一段没怎么理解,jsx代码执行了怎么就会调用notify函数呢?那我们来看一下这段jsx代码:

这段代码<h2 onClick={notify()}>...</h2>,这段代码其实再简单不过了,就是一个标签嘛。但是onClick={notify()}这一步是肯定要被执行的吧,不执行的话onClick属性没有意义啊。

而且notify(){}括起来了这代表这什么啊?我们回忆一下前面所说的,{}是为了引入js表达式。所以说这一步就是一个赋值表达式啊,notify()这个表达式就是在调用notify函数。这一段就是把notify函数的返回值赋值给onClick,所以说会出现这种在还没有渲染页面就出现函数调用的情况。

而且我们再看一下notify函数的代码:

function notify() {
    console.log("标题被点击了!");
}

这玩意儿有返回值吗?当然有,undefined就是它的返回值啊。也就是说在渲染出来的页面中<h2>标签的onclick属性将是undifined,这也就意味着接下来页面上的<h2>标签再怎么点击都不会有反应,因为你点击调用的是undefined

那么我们怎么处理呢?

早在上一次报错官方就已经暗示我们了,onClick得是一个函数。那么怎么才能是函数呢?那把后面的小括号去掉不就得了嘛。我们再来看看:

class Weather extends React.Component {
  ...
    render() {
        return <h2 onClick={notify}>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h2>
    }
}
ReactDOM.render(<Weather/>, document.getElementById("test"));

function notify() {
    console.log("标题被点击了!");
}

这么一写,我的onClick属性就被赋值为notify这个函数,而不是notify函数的返回值了。那么我们再来看一下最终的结果:

image-20211216154632606

页面渲染出来了,我还没有点击,控制台也什么都没有。

image-20211216154728517

我多次点击,控制台也统计出了我的点击次数。

以上便是react添加点击事件的方法。但是这样的话我们是不是就可以修改state从而驱动页面了呢?大家可以先试一下,我们接下来的内容再深入讨论。

总结

  • 原生js添加点击事件有三种方法
  • addEventListener方法
  • onclick方法
  • onclick属性配合回调函数
  • react中允许使用原生js中的方法
  • react不推荐使用addEventListeneronclick,大力推崇onClick属性配合回调函数
  • reactonClick属性中click首字母必须大写
  • onClick属性不能用引号括起来,需要用{}括起来
  • onClick属性必须是函数,所以函数名后面不可以加()

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/react_event_bind/