事件

这里的事件,指的是 React 内部封装 DOM 组件中的事件,如 onClick, onFocus等,而非我们自己通过 props 传递的属性,并在子组件中手动触发的事件

实例

import React, { Component } from 'react';

class TodoList extends Component {
    render() {
        return (
            <div>
                <input type="text" />
                
                <button onClick={()=>{
                    alert(1)
                }}>add</button>

                <button onClick={this.handelClick}>add2</button>
                
                <button onClick={this.handelClick2}>add3</button>

                <button onClick={()=>{
                   this.handelClick3()
                }}>add4</button>
            </div>
        );
    }

    handelClick () {
        console.log(1)
        console.log(this)
    }

    // 箭头函数 
    handelClick2 = () => {
        console.log(2)
        console.log(this)
    }

    // 箭头函数 
    handelClick3 = () => {
        console.log(3)
        console.log(this)
    }
}

export default TodoList;

react事件声明

  1. react事件采用驼峰式命名
  2. react事件接收一个函数声明不是函数调用的形式
//原生
<div onclick="handleClick()"></div>
//react
<div onClick={this.handleClick}></div>

绑定事件

采用on+事件名的方式来绑定一个事件.

注意,这里和原生的事件是有区别的:
原生的事件全是小写onclick , React里的事件是驼峰 onClick ,React的事件并不是原生事件,而是合成事件

事件handler的写法

  • 直接在render里写行内的箭头函数(不推荐)
  • 在组件内使用箭头函数定义一个方法(推荐)
  • 直接在组件内定义一个非箭头函数的方法,然后在render里直接使用 onClick={this.handleClick.bind(this)} (不推荐)
  • 直接在组件内定义一个非箭头函数的方法,然后在constructor里bind(this)(推荐)

Event 对象

和普通浏览器一样,事件handler会被自动传入一个 event 对象,这个对象和普通的浏览器 event 对象所包含的方法和属性都基本一致。不同的是React中的 event 对象并不是浏览器提供的,而是它自己内部所构建的。它同样具有 event.stopPropagation event.preventDefault 这种常用的方法.

this问题

实例

import React, { Component } from 'react';

class TodoList extends Component {
    render() {
        return (
            <div>
                <input type="text" />

                {/* :如果逻辑过不多,此写法推荐 */}
                {/* 可以直接访问this,无需手动绑定 */}
                <button onClick={()=>{ 
                     console.log(0)
                     console.log(this)
                }}>add</button>

                {/* :此写法不推荐 */}
                {/* 不可以直接访问this,需手动绑定 --  .bind(this)*/}
                <button onClick={this.handelClick.bind(this)}>add2</button>
                
                {/* :此写法推荐 */}
                {/* 可以直接访问this,无需手动绑定,handelClick2是箭头函数,可以绑定外部this :此写法推荐 */}
                <button onClick={this.handelClick2}>add3</button>

                {/* :此写法比较推荐,传参数很方便 */}
                {/* 可以直接访问this,无需手动绑定,onClick调用的是箭头函数,可以绑定外部this */}
                <button onClick={(e)=> this.handelClick3(e)}>add4</button>
            </div>
        );
    }

    handelClick () {
        console.log(1)
        console.log(this)
    }

    // 箭头函数 
    handelClick2 = (evt) => {
        console.log(2)
        console.log(this)
        
        // 打印 Event 对象
        console.log(evt)
    }

    // 箭头函数 
    handelClick3 = (evt) => {
        console.log(3)

        // 打印 Event 对象
        console.log(evt)
    }
}

export default TodoList;

为什么使用bind绑定this

class组件的事件绑定this问题

1. class的方法默认不会绑定 this,如果没有绑定 this.handleClick的this 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。

class A extends React.Component{
    constructor(props){
        super(props)    
    }
    handleClick(){
        //class的方法默认不会绑定 `this`,this的指向根据调用的方式判断
        //没有绑定调用的话this为undefined
        this.setState({a:1})
    }
    render(){
        return (
        <div onClick={this.handleClick}></div>
        )
    }
}

this问题的解决方式有三种

(1)在constructor里使用bind为方法绑定this

class A extends React.Component{
    constructor(props){
        super(props)
        this.handleClick= this.handleClick.bind(this);    // 注意此处
    }
    handleClick(){
        this.setState({a:1})
    }
}

(2)在元素上绑定事件时使用箭头函数


class A extends React.Component{
    constructor(props){
        super(props)    
    }
    handleClick(){
        //class的方法默认不会绑定 `this`,this的指向根据调用的方式判断
        //没有绑定调用的话this为undefined
        this.setState({a:1})
    }
    render(){
        //render里的this指向自身
        return (
        <div onClick={()=> this.handleClick() }></div>
        )
    }
}

(3)使用箭头函数声明方法

class A extends React.Component{
    constructor(props){
        super(props)    
    }
    handleClick=()=>{
        //箭头函数的this由父作用域的this判断
        this.setState({a:1})
    }
    render(){
        //render里的this指向自身
        return (
        <div onClick={this.handleClick}></div>
        )
    }
}

react事件传递参数

1.要在绑定事件的位置给事件传参有两种方式,

(1)通过bind,使用bind会隐式传入事件对象e,作为函数的最后一个参数。

<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

(2)通过箭头函数,使用箭头函数需要主动传入事件对象e 。

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

事件说明

React 根据 W3C 规范定义了合成事件 (SyntheticEvent),我们就无需担心跨浏览器的兼容性问题
React 出于性能与事件管理方面的考量,在之前的 React 版本中 (v17 之前)

  • 会将在 JSX 中注册的事件收集到 document 上 (即通过事件委托,在 document 上注册事件,等触发事件时,则按虚拟 DOM 树的结构进行事件触发机制去分发事件);
  • 几乎所有的事件处理,均在 document 的事件中处理

    • 比如 onFocus 等事件不会冒泡的事件,就不做委托,直接在元素上监听
    • 一些 document 上没有的事件,也直接在元素上监听 (如:audio、video标签的事件等)
  • 在 document 中的事件处理,会根据虚拟 DOM 树的结构完成事件函数的调用,默认是冒泡机制 (事件捕获要通过类似 onClickCapture 的方式注册)
  • React 的事件参数,并非真实的事件参数,而是 React 合成的一个对象 (SyntheticEvent)

    • 通过调用 e.stopPropagation() 阻止事件冒泡 (仅阻止 React 事件)
    • 通过 e.nativeEvent 可以得到真实的 DOM 事件对象 (不过很少会用到)
    • 为了提高效率,React 使用事件对象池来处理事件对象 (即事件对象会重用)
  • 在 React 17 之后,事件委托的节点就转移到了渲染的根节点上,而且也帮我们解决了此类关于事件冒泡的问题 (本文测试用例则说明,对于使用 ReactDOM.createPortal 创建的组件,表现上略有差异)

注意点

  • 若给真实 DOM 注册事件,并阻止冒泡,则很有可能导致 React (JSX) 中注册的相关事件无法触发
  • 若给真实 DOM 注册事件,它会先于 React 事件执行 (即通过 onClickdom.addEventListener 绑定的事件,真实 DOM 事件会先执行;因为这个元素被点击时,真实 DOM 事件会很快找到,而 React 绑定的事件则需要去找到事件委托的元素,再去调用当前点击元素绑定的事件函数)
  • 过 React 事件阻止事件冒泡,无法阻止真实 DOM 事件的冒泡
  • 可以使用 e.nativeEvent.stopImmediatePropagation() 去阻止 document 上剩余的事件处理程序的运行 (当我们在使用某些第三方库,在这个库有可能使用了一些事件处理,也对 document 绑定过点击事件,如: document.addEventListener("click", handler))
  • 在事件处理程序中,不要异步使用事件对象 e;如果一定有异步使用的需求,则需要调用 e.persist() 函数持久化保存此事件对象 (代价自然是损耗效率的)

React 事件总结

绑定事件处理函数

1.1 鼠标类

  1. onContextMenu
  2. onClick
  3. onDoubleClick
  4. onMouseDown
  5. onMouseUp
  6. onMouseEnter
  7. onMouseLeave
  8. onMouseMove
  9. onMouseOut
  10. onMouseOver

1.2 拖拽事件:

  1. onDrop
  2. onDrag
  3. onDragStart
  4. onDragEnd
  5. onDragEnter
  6. onDragLeave
  7. onDragOver
  8. onDragExit

1.3 触摸

触摸只会在移动设备上产生

  1. onTouchStart
  2. onTouchEnd
  3. onTouchMove
  4. onTouchCancel

1.4 键盘

onKeyPress是onKeyDown和onKeyUp的组合

  1. onKeyPress
  2. onKeyDown
  3. onKeyUp

剪切类

对应的是我们常常使用的复制剪切粘贴

  1. onCopy
  2. onCut
  3. onPaste

表单类

  1. onChange
  2. onInput
  3. onSubmit
  4. onChange可以用在输入框、单选框、下拉列表里,每当内容发生变化时我们都能获得通知。

onInput使用在文字输入。

onSubmit是用在整个表单的输入提交,常用在禁止表单的默认操作。

1.7 焦点事件

  1. onFocus
  2. onBlur

1.8 UI元素类

  1. onScroll

滚动事件触发的时候会触发onScroll事件

1.9 滚动

  1. onWheel

鼠标滚轮触发的事件,监听滚动幅度,滚动方位

1.10 组成事件

  1. onCompositionEnd
  2. onCompositionStart
  3. onCompositionUpdate

1.11 图片类

  1. onLoad
  2. onError

1.12 多媒体类

  1. onAbort
  2. onCanPlay
  3. onCanPlayThrough
  4. onDurationChange
  5. onEmptied
  6. onEncrypted
  7. onEnded
  8. onError
  9. onLoadedData
  10. onLoadedMetadata
  11. onLoadStart
  12. onPause
  13. onPlay
  14. onPlaying
  15. onProgress
  16. onRateChange
  17. onSeeked
  18. onSeeking
  19. onStalled
  20. onSuspend
  21. onTimeUpdate
  22. onVolumeChange
  23. onWaiting

事件池

虚拟事件对象已经被合并。这意味着虚拟事件对象将被重新使用,而该事件回调被调用之后所有的属性将无效。这是出于性能的考虑。因此,您不能以异步的方式访问事件。

function onClick(event) {
  console.log(event); // =>无效的对象
  console.log(event.type); // => "click"
  var eventType = event.type; // => "click"

  setTimeout(function() {
    console.log(event.type); // => null
    console.log(eventType); // => "click"
  }, 0);

  this.setState({clickEvent: event}); // 不起作用.this.state.clickEvent 将只包含空值.
  this.setState({eventType: event.type}); // 您依然可以导出事件属性
}

如果您想以一个异步的方式来访问事件属性,您应该对事件调用event.persist()。这将从事件池中取出合成的事件,并允许该事件的引用,使用户的代码被保留。

事件对象

事件处理器将会传入SyntheticEvent的实例,一个对浏览器本地事件的跨浏览器封装。它有和浏览器本地事件有相同的属性和方法,包括stopPropagation()preventDefault(),但是没有浏览器兼容问题。

如果因为一些因素,需要底层的浏览器事件对象,只要使用nativeEvent属性就可以获取到它了。

对于 v0.14,在事件处理函数中返回 false 将不会阻止事件冒泡。取而代之的是在合适的应用场景下,手动调用e.stopPropagation()或者e.preventDefault()

handleChange:function(e){
    console.log(e.target.value);
}

其中target是 事件对象e 是事件对象的属性

通用属性

(以下内容括号内为类型)

  1. bubbles (boolean) 表示事件是否冒泡
  2. cancelable(boolean) 表示事件是否可以取消
  3. currentTarget(DOMEventTarget) 与Target类似,由于事件可以冒泡,所以两者表示的内容是不同的
  4. defaultPrevented(boolean) 表示事件是否禁止了默认行为
  5. eventPhase(number) 表示事件所处的阶段
  6. isTrusted(boolean) 表示事件是否可信。所谓的可信事件表示的是用户操作的事件,不可信事件就是通过JS代码来触发的事件。
  7. nativeEvent(DOMEvent)
  8. preventDefault() (void) 对应的defaultPrevented,表示的是禁止默认行为
  9. stopPropagaTion() (void) 对应的是bubbles,表示的是sh
  10. target(DOMEventTarget)
  11. timeStamp(number) 时间戳,也就是事件触发的事件
  12. type(string) 事件的类型

不同事件对象的特有属性

剪切事件
  1. clipboardData(DOMDataTransfer)表示拿到的数据
键盘事件
  1. ctrlKey(boolean) 表示是否按下ctrl键
  2. altKey(boolean) 表示是否按下alt键
  3. shiftKey(boolean) 表示是否按下shift
  4. metaKey(boolean) 表示的是win系统下的win键,mac系统下对应的command键
  5. getModifierState(key) (function) 表示是否按下辅助按键(辅助按键就是雷士ctrl、shift等辅助按键)可以传入按键编码来判断是否按下
  6. charCode(Number) 表示的是按键的字符编码,可以通过编码来判断按下的是什么键
  7. key(string) 字符串,按下的键
  8. keyCode(Number) 表示那些不是字符编码的按键
  9. which(Number) 表示经过通用化得charCode和keyCode
  10. locale(String) 表示本地化得一些字符串
  11. location(number) 表示位置
  12. repeat(boolean) 表示按键是否重复
焦点事件
  1. relatedTarget(DOMEventTarget) 相关焦点对象
鼠标事件
  1. ctrlKey(boolean)
  2. altKey(boolean)
  3. shiftKey(boolean)
  4. metaKey(boolean)
  5. getModifierState(key) (function)
  6. button(Number)
  7. buttons(Number)
  8. clientX(Number) 原点为浏览器左上角
  9. clinetY(Number) 原点为浏览器左上角
  10. pageX(Number) 原点为HTML页面的左上角
  11. pageY(Number) 原点为HTML页面的左上角
  12. screenX(Number) 原点为显示器的左上角
  13. screenY(Number) 原点为显示器的左上角
  14. relatedTarget(DOMEventTarget)
触摸事件

为了使触摸事件生效,在渲染所有组件之前调用 React.initializeTouchEvents(true)。

  1. ctrlKey(boolean)
  2. altKey(boolean)
  3. shiftKey(boolean)
  4. metaKey(boolean)
  5. getModifierState(key)
  6. changedTouches(DOMTouchList) 判断手势操作
  7. targetTouches(DOMTouchList) 判断手势操作
  8. touches(DOMTouchList) 判断手势操作
UI元素事件
  1. detail(Number) 滚动的距离
  2. view(DOMAbstractView) 界面,视窗
鼠标滚动
  1. deltaMode(Number) 可以理解为移动的单位
  2. deltaX(Number) X轴移动的相对距离固定值
  3. deltaY(Number) Y轴移动的相对距离固定值
  4. deltaZ(Number) Z轴移动的相对距离固定值

实例

滚动事件对象

var HelloDada = React.creatClass({
    getInitialState:function(){
        return {
            backgroundColor:'#FFFFFF'
        }
    },
    handleWheel:function(e){
        var newColor = (parseInt(this.state.backgroundColor.substr(1),16)+e.deltaY*997).tiString(16);
        this.setState({
            backgroundColor:newColor
        })
    },
    render:function(){
        return <div onWheel={this.handleWheel} style={this.state}> // 注意这里 onWheel
        <p>Dada Shuaige</p> 
        </div>
    }
});
ReactDOM.render(<HelloDada />,document.body)

键盘事件对象

var Dada =React.creatClass{
    getInitialState:function(){
        return{
            password:''
        }
    },
    handleKeyPress:function(e){
        this.setState({
            paddword:this.state.password+e.which
        });
    },
    handleChange:function(e){
        e.target.value='';
    },
    render:function(){
        return <div>
        <input onKeyPress={this.handleKeyPress} onChange={this.handleChange} /> // 注意这里 onKeyPress
        <p style={{
            'display':this.state.password.indexOf('495051') >=0?'block':'none'
        }}>Dada handsomeboy</p>
        </div>
    }
};
ReactDOM.render(<Dada />,document.body)

事件与状态关联

状态不仅仅实现了组件内部结果的清晰对应,还实现了组件与用户之间的交互,使用户与组件的行为紧紧结合起来

handleChange:function(e){
    this.setState({Dada:e.target.value});
}

this.setState设置状态

实例

var Dada =React.creatClass({
    getInitialState:function(){
        return{
            x:0,
            y:0
        }
    },
    handleMouseMove:function(e){
        this.setState({
            x:e.clientX,
            y:e.clientY
        });
    },
    render:function(){
        return <div onMouseMove={this.handleMouseMove} style={{  // 注意这里 onMouseMove
            width:'200px',
            height:'200px',
            backgroundColor:'#999'
        }}>
        {this.state.x+'.'+this.state.y}
        </div>
    }
});
ReactDOM.render(<Dada />,document.body)

React绑定事件和原生绑定事件的区别

react事件和原生事件的区别是:

  • react中的事件是绑定到document上面 , React并不会真正的绑定事件到每一个具体《》的元素上,而是采用事件代理的模式:
  • 而原生的事件是绑定到dom上面

相对绑定的地方来说,dom上的事件要优先于document上的事件执行,react的事件对象是合成。

参考链接:


前端老兵
15 声望1 粉丝

你们说这是哪里?