您的当前位置:首页正文

react——事件绑定

2024-11-06 来源:个人技术集锦

        react有两种写法,一种是类组件,也叫有状态组件;一种是函数式组件,也叫无状态组件,而后引入了react hooks,函数式组件也能修改状态。在这两种组件中的事件绑定的原理一样,写法不一样。这篇文章主要就是记录这两种组件中的事件绑定写法。

 

一、前提说明

        重点是记录两种组件中事件绑定使用的区别,避免下次使用的时候还犯迷糊,在下面的写法中,可能出现同一类组件中多种写法,只要自己选择自己喜欢的那种写就可以了,没必要每种都要会,能看的懂就可以。想直接拿来用的,可以直接跳过原理,看使用案例,对原理感兴趣的可以回头来看看事件原理。在react中,事件的写法都是on+事件类型(第一个字母大写),如点击事件(onClick),鼠标事件(onMouseOver ,onMouseOut)

二、事件绑定的原理

        react中的事件都是合成事件,react并不是将click事件绑定到了div的真实DOM上,而是在document处监听了所有的事件,当事件发生并且冒泡到document处的时候,React将事件内容封装并交由真正的处理函数运行。这样的方式不仅仅减少了内存的消耗,还能在组件挂在销毁时统一订阅和移除事件。

        另外冒泡到document上的事件也不是原生的浏览器事件,而是由react自己实现的合成事件(SyntheticEvent)。因此我们如果不想要是事件冒泡的话调用event.stopProppagation()方法是无效的。而应该调用event.preventDefault()。

(一)事件注册

        组件更新或者装载时,在给dom增加合成事件时,需要将增加的target传入到document进行判断,给document注册原生事件回调为dispatchEvent(统一的事件分发机制)。

(二)事件存储

        EventPluginHub负责管理React合成事件的callback,它将callback存储到listennerBank中,另外还存储了负责合成事件的Plugin,Event存储到listennerbank中,每一个元素在listennerBank中会有唯一的key。

(三)事件触发执行

        点击时冒泡到docunment中,触发注册原生事件的回调dispatchEvent,获取到触发这个事件的最深层元素,事件执行利用react的批处理机制。

(四)合成事件

        循环所有类型的eventPlugin,对应每个事件类型,生成不同的事件池,如果是空,则生成新的,有则用之前的,根据唯一key获取到指定的回调函数,再返回带有参数的回调函数。

(五)总流程

 -- >  组件装载/更新

 -- >  新增/删除事件

 -- >  eventplugin添加到ListennerBank中监听事件

 -- >  触发事件

 -- >  生成合成事件

 -- >  通过唯一key获取到指定函数

 -->   执行指定回调函数

 -- >  执行完毕后释放

三、事件使用案例

(一)类组件的事件绑定

import React, { Component } from 'react'

export default class App1 extends Component {
    test2Fn() {
        console.log('test2');
    }
    test3Fn = () => {
        console.log('test3');
    }
    test4Fn = () => {
        console.log('test4');
    }
    render() {
        return (
            <div>
                {/* 直接写箭头函数,因为普通函数有this指向问题 */}
                <button onClick={() => { console.log('test1'); }}>第一种</button>
                {/* this.test2Fn()会被直接调用 和 this.test2Fn不一样*/}
                <button onClick={this.test2Fn}>第二种</button>
                <button onClick={this.test3Fn}>第三种</button>
                <button onClick={() => {
                    this.test4Fn()
                }}>第四种</button>

            </div>
        )
    }
}

个人比较喜欢第三种写法

第一种写法有弊端,逻辑长的时候不方便观看,也不建写在dom里面

第二种有this指向问题,如定义一个a=100,在方法二中通过this.a获取会报错,这是因为this没有指向App1实例中

 通过this.test2Fn.bind(this)可以解决,这样可以使两个this指向一致

import React, { Component } from 'react'

export default class App1 extends Component {
    a=100  //添加一个全局变量
    test2Fn() {
        console.log('test2',this.a);
    }
    test3Fn = () => {
        console.log('test3',this.a);
    }
    test4Fn = () => {
        console.log('test4',this.a);
    }
    render() {
        return (
            <div>
                {/* 直接写箭头函数,因为普通函数有this指向问题 */}
                <button onClick={() => { console.log('test1',this.a); }}>第一种</button>
                {/* this.test2Fn()会被直接调用 和 this.test2Fn不一样*/}
                <button onClick={this.test2Fn.bind(this)}>第二种</button>
                <button onClick={this.test3Fn}>第三种</button>
                <button onClick={() => {
                    this.test4Fn()
                }}>第四种</button>

            </div>
        )
    }
}

this指向修改

call:    修改this指向,并使函数自动执行(不用函数()调用)

apply: 修改this指向,并使函数自动执行(不用函数()调用)

bind:   修改this指向,需要手动执行(用()调用)

(二)函数式组件的事件绑定

function App2(){
    const test=()=>{
        console.log("add");
    }
    return(
        <div >
            <button onClick={test}>add</button>
        </div>
    )
}
export default App2

Top