詭異!React stopPropagation失靈

前言

先來看這
故事的開頭是這樣的:寫代碼的過程當中,發如今內層用stopPropagation阻止綁定在document上的事件的時候,是沒辦法作到的,只能夠阻止outClick事件的觸發。react

class ExampleApplication extends React.Component {
    componentDidMount() {
        document.addEventListener('click', () => {
            alert('document click');
        })
    }

    outClick(e) {
        console.log(e.currentTarget);
        alert('outClick');
    }
    
    onClick(e) {
        console.log(e.currentTarget);
        alert('onClick');
        e.stopPropagation();
    }
    render() {
        return <div onClick={this.outClick}>
            <button onClick={this.onClick}> 測試click事件 </button>
        </div>
    }
}

關於這個問題的解釋,網上五花八門,有些將其歸結爲是因爲事件委託的緣由,例如這篇文章裏說segmentfault

咱們直接在jsx模板上綁定的事件,都是委託在了document上,那天然要比直接在dom上綁定的事件慢了,等document收到事件後纔去e.stopPropagation(),太晚了

其實從上面例子的輸出: 由'onClick',再到'document click'可知,其實原生document上綁定的事件時最後執行的,因此並非由於document收到事件快慢的緣由而致使這個問題。瀏覽器

真相

真相只有一個,那就是:
出現上述bug的主要緣由是混用瀏覽器原生事件跟React合成事件dom

詳細解釋:React有本身的一套事件處理機制,它會將全部的事件都綁定在document上,而後再用dispatchEvent統一分發,這時候分發的是合成事件
onClick(e)這時候拿到的e實際上是合成事件,只能阻止合成事件的冒泡。函數

舉個栗子來驗證一下

class ExampleApplication extends React.Component {
    componentDidMount() {
        document.addEventListener('click', () => {
            alert('document click');
        })
        document.getElementById('div1').addEventListener('click', () => {
            alert('原生outClick');
        })
    }

    outClick(e) {
        console.log(e.currentTarget);
        alert('合成outClick');
    }
    
    onClick(e) {
        console.log(e.currentTarget);
        alert('onClick');
        e.stopPropagation();
    }
    render() {
        return <div id="div1" onClick={this.outClick}>
            <button onClick={this.onClick}> 測試click事件 </button>
        </div>
    }
}

作了點小改動,就是外層的div綁定的函數用原生的方式跟jsx的方式都綁定一次,因此最終的輸出爲測試

'原生outClick', 'onClick','document click'

因此button回調函數裏的stopPropagation只能阻止合成事件的冒泡,而對於原生綁定的,則不行。this

解決方法

解決方法有幾種,我我的認爲最簡單的就是直接在onClick裏利用[event.stopImmediatePropagation()][2]。原理是這樣的:spa

對於例子一里,document其實綁定了兩個事件:code

// react 合成事件, dispatchEvent裏面執行回調函數
document.addEventListener('click', dispatchEvent);

// 瀏覽器原生
document.addEventListener('click', () => {
   alert('document click');
})

dispatchEvent裏的stopImmediatePropagation可使得綁定在document上的其餘事件就不會被觸發component

onClick(e) {
        console.log(e.currentTarget);
        alert('onClick');
        e.nativeEvent.stopImmediatePropagation();
    }

固然本篇文章只是爲了拋磚引玉,重點是下一篇的react事件機制,歡迎繼續收看。

相關文章
相關標籤/搜索