React事件处理

React事件处理

在之前的博文中,我们知道怎么给react的class组件的 dom绑定事件了,现在我们在一起实现给react DOM绑定事件

给函数组件绑定事件

function FnEvent(){
	function handleClick(e){
		console.log('this is handle FnEvent')
	}
	return (
		<button onClick={handleClick}>FnEvent</button>
	)
}
ReactDOM.render(<FnEvent />,document.getElementById('function-event'))

给class组件绑定事件

class ButtonEvent extends React.Component{
	constructor(){
		super()
		this.handleClick = this.handleClick.bind(this)
	}
//	componentDidMount(){
//		console.log(this)
//	}
	render(){
		return (
			<button onClick={this.handleClick}>classEvent</button>
		)
	}
	handleClick(){
		console.log(this)
	}
}
ReactDOM.render(<ButtonEvent />,document.getElementById('class-event'))

通过上面的代码实现,我们可以发现当我们没有给事件函数绑定组件的this上下文的时候,是访问this的时候,是
undefined,因此就有了以下问题

  • 为什么给react DOM绑定事件函数,事件函数为什么访问不到组件this?

react class组件访问this

当我们用class创建一个类的时候,实例化对象后调用其静态或者原型方法的时候,如果该对象没有this
值(或this作为布尔,字符串,数字,未定义或null),那么this值在被调用的函数内部将为undefined
不会发生自动包装。

class Animal{
	speak(){
		return this
	}
	static eat(){
		return this
	}
}
let animal1 = new Animal();
console.log(animal1.speak());//Animal {}
let speck = animal1.speak
console.log(speck())//undeifined
console.log(Animal.eat());
/*
 	class Animal{
		speak(){
			return this
		}
		static eat(){
			return this
		}
	}
 */
let eat = Animal.eat;
console.log(eat());//undefined

在上述代码中,在类里面声明了一个handleClick函数,然后给按钮绑定事件的时候相当于隐式的将这个函数赋值给
onClick的回调函数callback,这样的话handClick就失去了上下文,就访问不到this

解决访问不到this的方法

  • 在constructor里面使用bindthis.handleClick = this.handleClick.bind(this)
  • <button onClick={this.handleClick.bind(this)}></button>
  • 箭头函数<button onClick={(e) => this.handleClick(e)}></button>

原生事件和react事件

例子1

class EventPrinciple extends React.Component{
	innerClick = e =>{
		console.log('A: react inner click');
	};
	outerClick = () =>{
		console.log("B: react outer click")
	};
	componentDidMount(){
		document.getElementById('outer').addEventListener('click',()=>console.log('C:native outer click'));
		window.addEventListener('click',()=>
			console.log("D:native window click")
		)
	}
	render(){
		return(
			<div id='outer' onClick={this.outerClick}>
				<button id='inner' onClick={this.innerClick}>BUTTON</button>
			</div>
		)
	}
}
ReactDOM.render(<EventPrinciple />,document.getElementById('event-principle'));

上面的代码中,我们在react组件挂载后给'outer'绑定了原生事件,同时绑定了react事件,给window绑定了原生事件,给'inner'
绑定了react事件,当把react渲染到页面中的时候执行结果如下所示:

由上面的结果,我们可以看出,react事件和原生事件是有区别的

并且我们可以知道React时间和原生事件的执行顺序,由此,我们可以理解:

  • react的所有事件都挂载在document中
  • 当真是dom触发后冒泡到document后才会对react事件进行处理
  • 然后执行react合成事件
  • 最后执行真正在document上挂载的事件

innerClick中的e.stopPropagation()加上,输出的结果如下

例子二

class EventPriciple1 extends React.Component{
	constructor(props){
		super(props);
		this.state = {
			editable:false
		}
	}
	handleClick = () => {
		console.log('edit button click!!!');
		this.setState({
			editable:true
		})
	};
	handleSubmit = e => {
		console.log('submit event!!');
		e.preventDefault();//避免页面刷新
	};
	render(){
		return (
			<form onSubmit={this.handleSubmit}>
				{this.state.editable?(<button type="submit">submit</button>):(
					<button type="button" onClick={this.handleClick}>edit</button>
				)}
			</form>
		)
	}
}
ReactDOM.render(<EventPriciple1 />,document.getElementById('event-principle1'))

这段代码中,我们给from绑定了一个react事件,在editable为true给按钮(type为button)绑定react事件handleClick
点击type为submit的按钮结果如下

edit button click
submit event

点击type为button的按钮结果如下

submit event

从上面两个例子中,我们产生以下两个疑问

  • react事件和原生事件有什么区别
  • 为什么react需要自己实现一个事件系统
  • react事件的事件机制

js事件

DOM事件流

时间流包括三个阶段,简而言之:时间一开始从文档的根节点流向目标对象(捕获阶段),
然后在目标对象上被触发(目标阶段),之后在回溯到文档的根节点(冒泡阶段)。

DOM0级处事件处理程序

<input type="button" value="Click me" id="btn">
<script>
	var btn = document.getElementById('btn');
	btn.onclick = function(){
		console.log(this.id)
	}
</script>

这里是将一个函数赋值给一个事件处理程序的属性,以这种方式添加的时间处理程序会在
时间流的冒泡阶段被处理。要删除事件将btn.onclick = null即可

DOM2级事件处理程序

DOM2级事件定义了addEventListener()removeEventListener()
两个方法,用于处理和删除事件处理程序的操作

所有DOM节点都包含这两个方法,它们接受3个参数:要处理的事件名、作为事件
处理程序的函数和一个布尔值。最后的布尔值参数是true表示在捕获阶段调用事件处理程序,
如果是false(默认)表示在冒泡阶段调用事件处理程序

<input type='button' value='Click me' id='btn' />
<script>
	var btn = document.getElementById('btn');
	btn.addEventListener('click',function(){
		console.log(this.id)
	},false)
	btn.addEventListener('click',function(){
		console.log('hello word')
	},false)
	
</script>

上面代码两个事件处理程序会按照他们的添加顺序触发,但是如果用的是DOM0
级绑定的事件处理程序,后面的将会覆盖前面的

通过 addEventListener添加的时间处理程序只能使用removeEventListener
来移除,移除时候传入参数与添加时候的参数相同,即使匿名函数无法被移除

<input type='button' value='Click me' id='btn' />
<scirpt>
	var btn = document.getElementById('btn');
	var handler = function(){
		console.log(this.id);
	}
	btn.addEventListener('click',handler,false);
	btn.removeEventListener('click',handler,false);
</script>

IE事件处理程序

IE通常都是特立独行的,它添加和删除事件处理程序的方法分别是
attachEventdetachEvent

同样接受事件处理程序名称与事件处理程序函数两个参数,但跟addEventListener
的区别是:

  • 事件名称需要添加'on',比如'onclik'
  • 没了第三个布尔值,IE8及更早的版本只支持事件冒泡
  • 依然可以添加多个处理程序,但触发顺序是反着来的

还有一点需要注意,DOM0和DOM2级的方法,其作用域都是在其所依附的
元素中,attachEvent则是全局,即如果像之前一样使用this.id
访问到的就不是button元素,而是window,就得不到正确的结果

react事件和原生事件有什么区别?

  • React时间使用驼峰命名,而不是全部小写
  • 通过JSX,你传递一个而函数作为事件处理程序,而不是一个字符串

HTML

<button onclick='handleClick'>BUTTON</button>

React

<button onClick={handleClick}>BUTTON</button>

另一个区别是,在React中你不能通过返回false来阻止默认行为。必须明确调用preventDefault

react事件和原生事件可以混用吗?

react事件和原生事件最好不要混用。原生事件中如果执行了stopPropagation方法,
则会导致其他react事件失效。因为所有元素的事件将无法冒泡到document上。由此,react事件都将无法被
注册。

由上面的例子,我们可以看出React自己实现了一套时间机制,自己模拟了时间冒泡和捕获的过程,采用了事件
代理,批量更新的方法,并且抹平了各个浏览器的兼容性的问题。

react事件的事件机制

react事件机制

posted @ 2020-04-07 21:10  upupupupupgo  阅读(184)  评论(0编辑  收藏  举报