上篇文章中,我們談到了React事件系統(tǒng)的實(shí)現(xiàn)方式嘿悬,和在React中使用原生事件的方法低零,那么這篇文章我們來(lái)繼續(xù)分析下婆翔,看看React中合成事件和原生事件混用的各種情況。
上一個(gè)例子
在上篇文章中掏婶,我們舉了個(gè)例子啃奴。為了防止大家不記得,我們來(lái)看看那個(gè)例子的代碼雄妥。
class App extends React.Component {
constructor(props){
super(props);
this.state = {
show: false
}
this.handleClick = this.handleClick.bind(this)
this.handleClickImage = this.handleClickImage.bind(this);
}
handleClick(){
this.setState({
show: true
})
}
componentDidMount(){
document.body.addEventListener('click', e=> {
this.setState({
show: false
})
})
}
componentWillUnmount(){
document.body.removeEventListener('click');
}
handleClickImage(e){
console.log('in this ')
e.stopPropagation();
}
render(){
return (
<div className="container">
<button onClick={this.handleClick}>Open Image</button>
<div className="img-container" style={{ display: this.state.show ? 'block': 'none'}} onClick={this.handleClickImage}>
![](http://upload-images.jianshu.io/upload_images/65230-3ad92c3b2a23e6b5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
這有什么問(wèn)題呢最蕾? 問(wèn)題就在于,如果我們點(diǎn)擊image的內(nèi)部依舊可以收起Image老厌,那么這是為什么呢揖膜?這是因?yàn)槲覀兗皶r(shí)點(diǎn)擊了Image的內(nèi)部,body上綁定的事件處理器依舊會(huì)執(zhí)行梅桩,這樣就讓我們的image收起來(lái)了。那我們?nèi)绻幌胱宨mage收起來(lái)改怎么做呢拜隧?
首先的想法是停止冒泡宿百,如果我們?cè)趇mg-container中就停止冒泡了是不是就可以讓image不消失了呢?比如這樣:
...
handleClickImage(e){
e.preventDefault();
e.stopPropagation();
}
render(){
return (
<div className="container">
<button onClick={this.handleClick}>Open Image</button>
<div className="img-container" style={{ display: this.state.show ? 'block': 'none'}} onClick={this.handleClickImage}>
![](http://upload-images.jianshu.io/upload_images/65230-3ad92c3b2a23e6b5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</div>
</div>
)
}
...
在這里我們定義一個(gè)handleClickImage的方法洪添,在其中我們執(zhí)行取消默認(rèn)行為和停止冒泡垦页。那是似乎效果并不是我們想要的。因?yàn)樽柚筊eact事件冒泡的行為只能用于React合成事件中干奢,沒(méi)法阻止原生事件的冒泡痊焊。同樣用React.NativeEvent.stopPropagation()也是無(wú)法阻止冒泡的。
如何解決這樣的問(wèn)題呢忿峻?首先薄啥,盡量的避免混用合成事件和原生事件。需要注意的點(diǎn)是:
- 阻止react 合成事件冒泡并不會(huì)阻止原生時(shí)間的冒泡逛尚,從上邊的例子我們已經(jīng)看到了垄惧,及時(shí)使用stopPropagation也是無(wú)法阻止原生時(shí)間的冒泡的。
- 第二點(diǎn)需要注意的是绰寞,取消原生時(shí)間的冒泡會(huì)同時(shí)取消React Event到逊。并且原生事件的冒泡在react event的觸發(fā)和冒泡之前。同時(shí)React Event的創(chuàng)建和冒泡是在原生事件冒泡到最頂層的component之后的滤钱。我們來(lái)看這個(gè)例子:
class App extends React.Component {
render(){
return <GrandPa />;
}
}
class GrandPa extends React.Component {
constructor(props){
super(props);
this.state = {clickTime: 0};
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
console.log('React Event grandpa is fired');
this.setState({clickTime: new Date().getTime()})
};
componentDidMount(){
document.getElementById('grandpa').addEventListener('click',function(e){
console.log('native Event GrandPa is fired');
})
}
render(){
return (
<div id='grandpa' onClick={this.handleClick}>
<p>GrandPa Clicked at: {this.state.clickTime}</p>
<Dad />
</div>
)
}
}
class Dad extends React.Component {
constructor(props){
super(props);
this.state = {clickTime:0};
this.handleClick=this.handleClick.bind(this);
}
componentDidMount(){
document.getElementById('dad').addEventListener('click',function(e){
console.log('native Event Dad is fired');
e.stopPropagation();
})
}
handleClick(){
console.log('React Event Dad is fired')
this.setState({clickTime: new Date().getTime()})
}
render(){
return (
<div id='dad' onClick={this.handleClick}>
<p>Dad Clicked at: {this.state.clickTime}</p>
<Son />
</div>
)
}
}
class Son extends React.Component {
constructor(props){
super(props);
this.state = {clickTime:0};
this.handleClick=this.handleClick.bind(this);
}
handleClick(){
console.log('React Event Son is fired');
this.setState({clickTime: new Date().getTime()})
}
componentDidMount(){
document.getElementById('son').addEventListener('click',function(e){
console.log('native Event son is fired');
})
}
render(){
return (
<div id="son">
<p onClick={this.handleClick}>Son Clicked at: {this.state.clickTime} </p>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
在這個(gè)例子中我們有三個(gè)component觉壶,Son Dad,Grandpa件缸。同時(shí)定義了React Event handler 和 native event handler铜靶,并在Dad的native Event handler中stopPropagation,當(dāng)我們點(diǎn)擊Son or Dad component的時(shí)候會(huì)發(fā)現(xiàn)停团,React Event handler并沒(méi)有被trigger旷坦。
console里的output為:
"native Event son is fired"
"native Event Dad is fired"
這就說(shuō)明native Event的停止冒泡可以阻斷所有的React Event掏熬。所以即使我們是在Dad上停止冒泡的,依舊阻斷了Son上的React Event秒梅。
同時(shí)如果我們把dad上的stopPropagation remove掉我們會(huì)看到如下結(jié)果:
"native Event son is fired"
"native Event Dad is fired"
"native Event GrandPa is fired"
"React Event Son is fired"
"React Event Dad is fired"
"React Event grandpa is fired"
這就說(shuō)明React的合成時(shí)間是在原生事件冒泡到最頂層組件結(jié)束后才創(chuàng)建和冒泡的旗芬,也是符合React的原理,因?yàn)樵谑菍?shí)現(xiàn)的時(shí)候React只是將一個(gè)Event listener 掛在了最頂層的組件上捆蜀,其內(nèi)部一套自己的機(jī)制進(jìn)行事件的管理疮丛。