天天看點

React合成事件(阻止冒泡stopImmediatePropagation)一、遇到的問題二、React 合成事件

文章目錄

  • 一、遇到的問題
    • 問題描述
    • 分析
  • 二、React 合成事件
    • 1.執行順序
    • 2.合成事件阻止冒泡
      • 2.1e.stopPropagation
      • 2.2 e.nativeEvent.stopImmediatePropagation
    • 3.this指向問題
        • 解決
    • 4.傳參
    • 5. 事件池(React 17 不再使用事件池)

一、遇到的問題

React 阻止冒泡失效

問題描述

  • 一個div彈框,點選按鈕時顯示,點選空白處隐藏
  • 在document上綁定隐藏事件
  • 點選顯示的按鈕需要阻止冒泡
  • 但是我使用e.stopPropagation();失效

分析

  • React元件類似onClick的事件是合成事件,本質上所有綁定是代理到document上的,所有綁定都會冒泡到document層去執行
  • A、阻止合成事件間的冒泡,用e.stopPropagation();
  • B、阻止合成事件與最外層document上的事件間的冒泡,用e.nativeEvent.stopImmediatePropagation();
  • stopImmediatePropagation() 方法阻止監聽同一事件的其他事件監聽器被調用。
  • 如果有多個相同類型事件的事件監聽函數綁定到同一個元素,當該類型的事件觸發時,它們會按照被添加的順序執行。如果其中某個監聽函數執行了 event.stopImmediatePropagation() 方法,則目前元素剩下的監聽函數将不會被執行。
  • C、阻止合成事件與除最外層document上的原生事件上的冒泡,通過判斷e.target來避免
componentDidMount() {
    document.body.addEventListener('click',e=>{
    // 通過e.target判斷阻止冒泡
                if(e.target&&e.target.matches('a')){
    return;
                }
    console.log('body');
            })
        }
    }
           

二、React 合成事件

React 元件綁定事件本質上是代理到 document 上。

也就是說,隻有當事件流冒泡到 document 上時,才會依次觸發 document 上綁定的事件。

  • SyntheticEvent 執行個體将被傳遞給你的事件處理函數,它是浏覽器的原生事件的跨浏覽器包裝器。除相容所有浏覽器外,它還擁有和浏覽器原生事件相同的接口
  • SyntheticEvent 是合并而來。這意味着 SyntheticEvent 對象可能會被重用,而且在事件回調函數被調用後,所有的屬性都會無效。出于性能考慮,你不能通過異步通路事件。
  • 如果你想異步通路事件屬性,你需在事件上調用 event.persist(),此方法會從池中移除合成事件,允許使用者代碼保留對事件的引用。(從 v17 開始,e.persist() 将不再生效,因為 SyntheticEvent 不再放入事件池中。)
  • 合成事件中的 currentTarget 指向目前 dom 元素,但是 nativeEvent 的 currentTarget 指向 document

1.執行順序

React 所有事件都挂載在 document 對象上;

當真實 DOM 元素觸發事件,會冒泡到 document 對象後,再處理 React 事件;

  1. 先執行原生事件,
  2. 然後處理 React 事件;
  3. 最後真正執行 document 上挂載的事件。

2.合成事件阻止冒泡

官網文檔描述了:

從 v0.14 開始,事件處理器傳回 false 時,不再阻止事件傳遞。你可以酌情手動調用 e.stopPropagation() 或

e.preventDefault() 作為替代方案。

也就是說,在 React 合成事件中,需要阻止冒泡時,可以使用 e.stopPropagation() 或 e.preventDefault() 方法來解決,另外還可以使用 e.nativeEvent.stopImmediatePropagation() 方法解決。

2.1e.stopPropagation

e.stopPropagation() 隻能阻止合成事件間冒泡,即下層的合成事件,不會冒泡到上層的合成事件。事件本身還都是在 document 上執行。是以最多隻能阻止 document 事件不能再冒泡到 window 上。

2.2 e.nativeEvent.stopImmediatePropagation

Event 接口的 stopImmediatePropagation() 方法阻止監聽同一事件的其他事件監聽器被調用。

在 React 中,e.nativeEvent 才是原生 DOM 事件的那個 event

該方法可以阻止監聽同一事件的其他事件監聽器被調用。

在 React 中,一個元件隻能綁定一個同類型的事件監聽器,當重複定義時,後面的監聽器會覆寫之前的。

事實上 nativeEvent 的 stopImmediatePropagation隻能阻止綁定在 document 上的事件監聽器。而合成事件上的 e.nativeEvent.stopImmediatePropagation() 能阻止合成事件不會冒泡到 document 上。

利用e.nativeEvent.stopImmediatePropagation解決我遇到的問題

點選div内部,由于不冒泡,會正常執行菜單點選。

點選div外部,執行document上事件,關閉div。

3.this指向問題

在 React 中,JSX 回調函數中的 this 經常會出問題,在 Class 中方法不會預設綁定 this,就會出現下面情況, this.funName 值為 undefined :

class App extends React.Component<any, any> {
  childClickFun = () => {
    console.log("React 事件");
  };
  clickFun() {
    console.log("React this 指向問題", this.childClickFun); // undefined
  }
  render() {
    return (
        <div onClick={this.clickFun}>React this 指向問題</div>
    );
  }
}
export default App;
           

解決

1.用bind綁定this

<div onClick={this.clickFun.bind(this)}>React this 指向問題</div>
           

2.箭頭函數(this指向定義時所處的外層執行環境的this)

箭頭函數相關文章

clickFun() {
    console.log("React this 指向問題", this.childClickFun); // undefined
  }
  render() {
    return (
        <div onClick={() => this.clickFun()}>React this 指向問題</div>
    );
  }
           

4.傳參

<div onClick={this.clickFun.bind(this,props)}>React this 指向問題</div>
           

或者

clickFun(props) {
    console.log("React this 指向問題", this.childClickFun); // undefined
  }
  render() {
    return (
        <div onClick={() => this.clickFun(props)}>React this 指向問題</div>
    );
  }
           

5. 事件池(React 17 不再使用事件池)

React 事件池僅支援在 React 16 及更早版本中,在 React 17 已經不使用事件池。

下面以 React 16 版本為例:

function handleChange(e) {
  console.log("原始資料:", e.target)
  setTimeout(() => {
    console.log("定時任務 e.target:", e.target); // null
    console.log("定時任務:e:", e); 
  }, 100);
}
function App() {
  return (
    <div className="App">
      <button onClick={handleChange}>測試事件池</button>
    </div>
  );
}

export default App;
           

可以看到輸出

React合成事件(阻止冒泡stopImmediatePropagation)一、遇到的問題二、React 合成事件

在 React 16 及之前的版本,合成事件對象的事件處理函數全部被調用之後,所有屬性都會被置為 null 。這時,如果我們需要在事件處理函數運作之後擷取事件對象的屬性,可以使用 React 提供的 e.persist() 方法,保留所有屬性:

// 隻修改 handleChange 方法,其他不變
function handleChange(e) {
  // 隻增加 persist() 執行
  e.persist();
  
  console.log("原始資料:", e.target)
  setTimeout(() => {
    console.log("定時任務 e.target:", e.target); // null
    console.log("定時任務:e:", e); 
  }, 100);
}
           

再看結果

React合成事件(阻止冒泡stopImmediatePropagation)一、遇到的問題二、React 合成事件

由于 Web 端的 React 17 不使用事件池,所有不會存在上述“所有屬性都會被置為 null”的問題。

參考文章

https://segmentfault.com/a/1190000038251163

本文連結https://blog.csdn.net/qq_39903567/article/details/115346338

繼續閱讀