React事件处理

1 React事件处理

React与DOM事件处理的不同之处:
(1)React事件的命名方式是小驼峰方式,DOM的命名方式是小写。例如:DOM的命名:onclick,React的命名:onClick。
(2)事件处理函数是以对象的方式赋值,而不是以字符串的方式赋值。例如:DOM以字符串方式:onclick = "handleClick()",React以对象方式:onClick = { handleClick }
(3)阻止默认事件的方式不同。DOM通过返回false来阻止:<a href="www.baidu.com" onclick="javascript:return false;">百度</a>,React需要显式调用e.preventDefault来阻止。

2 React中事件处理函数

2.1 使用ES6的箭头函数

1、在render函数中使用箭头函数

示例代码:点击按钮,在终端输出0

    <div id="root"></div>
    <script type="text/babel">
        // 获取div
        let root = document.getElementById("root");
        // 创建类组件
        class MyComponent extends React.Component {
            constructor(props) {
                super(props);
                this.state = { // 定义状态机
                    number: 0
                }
            }
            render() {
                return (
                    <div>
                        {/* this指向当前组件的实例 */}
                        <button onClick={(event) => console.log(this.state.number)}>点击</button>
                    </div>
                )
            }
        }
        // 渲染
        ReactDOM.render(<MyComponent />, root);
    </script>

优点:不用在构造函数中绑定this
缺点:当函数的逻辑比较复杂时,render就显得臃肿,无法直观的看到组件的UI结构,代码可读性差

2、使用class fields语法:将箭头函数赋给类的属性

示例代码:点击按钮在终端输出click

    <div id="root"></div>
    <script type="text/babel">
        // 获取div
        let root = document.getElementById("root");
        // 创建类组件
        class MyComponent extends React.Component {
            constructor(props) {
                super(props);
            }
            handleClick = () => {
                console.log("click");
            }
            render() {
                return (
                    <button onClick={this.handleClick}>点击</button>
                )
            }
        }
        // 渲染
        ReactDOM.render(<MyComponent />, root);
    </script>

优点:不用在构造函数中绑定this,在render函数中调用简单

2.2 在constructor函数中bind

在构造函数中进行绑定:将事件处理函数作为类的成员函数
注意:在定义事件处理函数时,是无法识别this(即this是undefined的),必须在构造函数中绑定this

示例代码:点击按钮,在终端输出click

    <div id="root"></div>
    <script type="text/babel">
        // 获取div
        let root = document.getElementById("root");
        // 创建类组件
        class MyComponent extends React.Component {
            constructor(props) {
                super(props);
                // 将事件响应函数绑定到当前组件的实例上
                this.handleClick = this.handleClick.bind(this);
            }
            // 编写事件响应函数
            handleClick() {
                console.log("click");
            }
            render() {
                return (
                    // this.handleClick后面不加(),否则代表不点击直接调用
                    <button onClick={this.handleClick}>点击</button>
                )
            }
        }
        // 渲染
        ReactDOM.render(<MyComponent />, root);
    </script>

优点:在render函数调用时不需要重新创建事件处理函数
缺点:当事件处理很多时,构造函数就显的很繁琐

2.3 在render函数中绑定this

示例代码:点击按钮,在终端输出click

    <div id="root"></div>
    <script type="text/babel">
        // 获取div
        let root = document.getElementById("root");
        // 创建类组件
        class MyComponent extends React.Component {
            constructor(props) {
                super(props);
            }
            // 编写事件响应函数
            handleClick() {
                console.log("click");
            }
            render() {
                return (
                    <button onClick={this.handleClick.bind(this)}>点击</button>
                )
            }
        }
        // 渲染
        ReactDOM.render(<MyComponent />, root);
    </script>

优点:在调用事件处理函数时,传参比较方便
缺点:每次调用render函数时都重新绑定,导致性能下降

2.4 几种方式的比较

影响constroctor函数中bind使用class fields语法render中使用箭头函数在render中使用bind
render时生成新函数
性能无影响无影响有影响有影响
可直接携带参数
简洁性不好

3 事件处理中传参

3.1 直接传递参数

1、在构造函数中给事件处理函数绑定this,调用事件处理函数时直接传参
注:在箭头函数中调用事件处理函数时不需要绑定this

示例代码:点击张三按钮,终端输出编号1,点击李四按钮,终端输出编号2

    <div id="root"></div>
    <script type="text/babel">
        // 获取div
        let root = document.getElementById("root");
        // 创建类组件
        class MyComponent extends React.Component {
            constructor(props) {
                super(props);
                this.state = {
                    list: [
                        {
                            id: 1,
                            name: "张三"
                        },
                        {
                            id: 2,
                            name: "李四"
                        }
                    ]
                }
                // 本例中该语句不需要,因为下面的箭头函数中已经绑定了this
                // this.handleClick = this.handleClick.bind(this);
            }
            handleClick(id) {
                console.log("编号" + id)
            }
            render() {
                // 获取状态属性值
                const { list } = this.state
                return (
                    <div>
                        {
                            list.map((item) => <button onClick={() => { this.handleClick(item.id) }}>{item.name}</button>)
                        }
                    </div>
                )
            }
        }
        // 渲染
        ReactDOM.render(<MyComponent />, root);
    </script>

在这里插入图片描述
2、在render函数中调用事件处理函数时进行this的绑定

示例代码:点击张三按钮,终端输出编号1,点击李四按钮,终端输出编号2

    <div id="root"></div>
    <script type="text/babel">
        // 获取div
        let root = document.getElementById("root");
        // 创建类组件
        class MyComponent extends React.Component {
            constructor(props) {
                super(props);
                this.state = {
                    list: [
                        {
                            id: 1,
                            name: "张三"
                        },
                        {
                            id: 2,
                            name: "李四"
                        }
                    ]
                }
                // 本例中该语句可以没有,因为下面的handleClick()函数中没有使用到this
                // this.handleClick = this.handleClick.bind(this);
            }
            handleClick(id) {
                console.log("编号" + id)
            }
            render() {
                // 获取状态属性值
                const { list } = this.state
                return (
                    <div>
                        {
                            list.map((item) => <button onClick={this.handleClick.bind(this, item.id)}>{item.name}</button>)
                        }
                    </div>
                )
            }
        }
        // 渲染
        ReactDOM.render(<MyComponent />, root);
    </script>

在这里插入图片描述

3.2 使用data自定义属性

在定义UI控件时使用data自定义属性,在事件处理函数中通过“e.target.dataset.属性名”来获取UI控件中的data属性值

示例代码:点击张三按钮,终端输出编号1,点击李四按钮,终端输出编号2

    <div id="root"></div>
    <script type="text/babel">
        // 获取div
        let root = document.getElementById("root");
        // 创建类组件
        class MyComponent extends React.Component {
            constructor(props) {
                super(props);
                this.state = {
                    list: [
                        {
                            id: 1,
                            name: "张三"
                        },
                        {
                            id: 2,
                            name: "李四"
                        }
                    ]
                }
            }
            handleClick(e) {
                // 事件处理函数,e表示触发事件的对象
                console.log("编号" + e.target.dataset.count)
            }
            render() {
                // 获取状态属性值
                const { list } = this.state
                return (
                    <div>
                        {
                            list.map((item) => <button
                                onClick={this.handleClick.bind(this)}
                                data-count={item.id} // 标签的自定义属性使用“data-”开头,后面跟着自定义属性名
                            >{item.name}</button>)
                        }
                    </div>
                )
            }
        }
        // 渲染
        ReactDOM.render(<MyComponent />, root);
    </script>

在这里插入图片描述

4 事件流

说明:
(1)React的事件流默认是冒泡
(2)React中使用捕获方式:事件类型后面加一个后缀Capture,例如:onClickCapture

示例:冒泡方式传递

    <div id="root"></div>
    <script type="text/babel">
        let root = document.getElementById('root')
        // 1.定义CSS样式
        const style = {
            child: {
                width: '100px',
                height: '100px',
                backgroundColor: 'red'
            },
            parent: {
                width: '150px',
                height: '150px',
                backgroundColor: 'blue'
            },
            ancestor: {
                width: '200px',
                height: '200px',
                backgroundColor: 'green'
            }
        }
        // 2.定义类组件
        class App extends React.Component {
            render() {
                return (
                    <div onClick={() => { console.log("ancestor") }} style={style.ancestor}>
                        <div onClick={() => { console.log("parent") }} style={style.parent}>
                            <div onClick={(e) => {
                                console.log("child");
                            }}
                                style={style.child}>
                            </div>
                        </div>
                    </div>
                )
            }
        }
        //3.渲染
        ReactDOM.render(<App />, root)
    </script>

点击红色区域,按照child、parent、ancestor的顺序在终端输出。
在这里插入图片描述
示例:捕获方式传递

        // 2.定义类组件
        class App extends React.Component {
            render() {
                return (
                    <div onClickCapture={() => { console.log("ancestor") }} style={style.ancestor}>
                        <div onClickCapture={() => { console.log("parent") }} style={style.parent}>
                            <div onClickCapture={(e) => {
                                console.log("child");
                            }}
                                style={style.child}>
                            </div>
                        </div>
                    </div>
                )
            }
        }

点击红色区域,按照ancestor、parent、child的顺序在终端显示。
在这里插入图片描述

5 事件委托

在合成事件系统中,所有事件都是绑定在document元素上,即,虽然我们在某个react元素上绑定了事件,但是,最后事件都委托给document统一触发。在合成事件中只能阻止合成事件中的事件传播
在这里插入图片描述
示例:基于上一部分的案例,阻止事件冒泡

        // 2.定义类组件
        class App extends React.Component {
            render() {
                return (
                    <div onClick={(e) => { console.log("ancestor") }} style={style.ancestor}>
                        <div onClick={(e) => { console.log("parent") }} style={style.parent}>
                            <div onClick={(e) => {
                                console.log("child");
                                e.stopPropagation(); // 阻止冒泡
                            }}
                                style={style.child}>
                            </div>
                        </div>
                    </div>
                )
            }
        }

点击红色区域,只输出child,成功阻止了事件冒泡。
在这里插入图片描述
执行流程如下:
在这里插入图片描述
可以看到,react 阻止的事件流,并没有阻止真正DOM元素的事件触发,当红色div元素被点击时,真正的元素还是按照冒泡的方式,层层将事件交给上级元素进行处理,最后事件传播到document,触发合成事件,在合成事件中,child触发时,e.stopPropagation();被调用,合成事件中的事件被终止。因此,合成事件中的stopPropagation无法阻止事件在真正元素上的传递,它只阻止合成事件中的事件流。相反,如果我们在红色的div上,绑定一个真正的事件,那么,合成事件则会被终止。

6 事件对象

虽然React事件是合成事件,但是在事件处理中是可以获取事件对象的。

1、在React事件处理中有一个全局事件对象:event对象,每次事件触发后(事件处理函数调用完成后),就会清空event对象,当下一次事件触发时重新获取该对象。该对象不是原生(DOM)的event对象,但是可以通过该对象获取原生对象的部分属性。

示例代码:

    <div id="root"></div>
    <script type="text/babel">
        // 获取div
        let root = document.getElementById("root");
        // 1.定义样式规则
        const style = {
            "mydiv": {
                width: '150px',
                height: '150px',
                backgroundColor: 'red'
            }
        }
        // 2.定义类组件
        class App extends React.Component {
            constructor(props) {
                super(props)
                this.state = {
                    x: 0,
                    y: 0
                }
            }
            render() {
                return (
                    <div>
                        <div style={style['mydiv']}  //以数组的方式设置样式
                            onClick={(event) => { //'event'对象不是原生的event,但是可以通过event来获取原生的事件对象的部分属性
                                console.log(event);
                            }}>
                            X: {this.state.x},Y: {this.state.y}
                        </div>
                    </div>
                )
            }
        }
        // 3.渲染
        ReactDOM.render(<App />, root)
    </script>

示例代码:将用户点击时的坐标在div元素中显示出来

    <div id="root"></div>
    <script type="text/babel">
        // 获取div
        let root = document.getElementById("root");
        //1.定义样式规则
        const style = {
            "mydiv": {
                width: '150px',
                height: '150px',
                backgroundColor: 'red'
            }
        }
        //2.定义类组件
        class App extends React.Component {
            constructor(props) {
                super(props)
                this.state = {
                    x: 0,
                    y: 0
                }
            }
            render() {
                return (
                    <div>
                        <div style={style["mydiv"]}  //以数组的方式设置样式
                            onClick={(event) => { //'event'对象不是原生的event,但是可以通过event来获取原生的事件对象的部分属性
                                this.setState({
                                    x: event.clientX,
                                    y: event.clientY
                                })
                            }}>
                            X: {this.state.x},Y: {this.state.y}
                        </div>
                    </div>
                )
            }
        }
        //3.渲染
        ReactDOM.render(<App />, root)
    </script>

在这里插入图片描述
合成事件中的event对象,并不是原生的event,只是说,我们可以通过它获取到原生event对象_上的某些属性,比如以上示例中的clientX和clientY。而且,对于这个event对象,在整个合成事件中,只有一个被全局共享,也就是说,当这次事件调用完成之后,这个event对象会被清空,等待下一次的事件触发,因此,我们无法在异步的操作中获取到event。

2、在异步操作中获取event。

示例代码:根据上面的示例进行修改

            render() {
                return (
                    <div>
                        <div style={style["mydiv"]}
                            onClick={(event) => {
                                setTimeout(() => {
                                    this.setState({
                                        x: event.clientX,
                                        y: event.clientY
                                    })
                                }, 1000);
                            }}>
                            X: {this.state.x},Y: {this.state.y}
                        </div>
                    </div>
                )
            }
        }

当用户点击div,一秒后可以看到控制台抛出错误:
在这里插入图片描述
问题:当事件触发、响应结束后,event对象会被清空,但是异步数据还没有得到,在得到异步数据之后再去访问event对象的属性就会报错。

解决办法:先将event对象的某些属性值保存起来,得到异步数据之后再来使用这些属性值

示例代码:

            render() {
                return (
                    <div>
                        <div style={style["mydiv"]}
                            onClick={(event) => {
                                const { clientX, clientY } = event;
                                setTimeout(() => {
                                    this.setState({
                                        x: event.clientX,
                                        y: event.clientY
                                    })
                                }, 1000);
                            }}>
                            X: {this.state.x},Y: {this.state.y}
                        </div>
                    </div>
                )
            }

这时点击div,在1秒后就会显示坐标值。

3、原生事件和合成事件的混合使用
React鼓励我们使用合成事件,但是,在某些需求中,还是需要通过原生事件来进行处理,这时就涉及到原生事件和合成事件的混合使用。

示例:用户点击按钮时,显示div,点击页面其他区域隐藏div

    <div id="root"></div>
    <script type="text/babel">
        class App extends React.Component {
            constructor(props) {
                super(props)
                this.state = {
                    isShow: 'none'
                }
            }
            button = React.createRef()  //创建了一个关联属性button
            componentDidMount() {
                document.addEventListener('click', e => {  //原生事件
                    /*
                        e.target表示触发click事件的对象
                        this.button.current表示当前的按钮
                    */
                    if (e.target != this.button.current) {
                        this.setState({
                            isShow: 'none'
                        })
                    }
                })
            }
            render() {
                return (
                    <div>
                        <button
                            ref={this.button}
                            onClick={() => { // 合成事件
                                this.setState({
                                    isShow: 'block'
                                })
                            }}>点击显示</button>
                        <br /> <br />
                        <div style={{
                            display: this.state.isShow,
                            width: '100px',
                            height: '100px',
                            backgroundColor: 'red'
                        }}></div>
                    </div>
                )
            }
        }
        //3.渲染
        ReactDOM.render(<App />, root)
    </script>

点击按钮显示div
在这里插入图片描述
点击空白处隐藏div
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值