Phản ứng ngăn chặn sự kiện sủi bọt trong các thành phần lồng nhau khi nhấp chuột


115

Đây là một thành phần cơ bản. Cả hai <ul><li>có chức năng onClick. Tôi chỉ muốn onClick on the <li>fire, không phải <ul>. Làm thế nào tôi có thể đạt được điều này?

Tôi đã chơi với e.preventDefault (), e.stopPropagation (), nhưng không có kết quả.

class List extends React.Component {
  constructor(props) {
    super(props);
  }

  handleClick() {
    // do something
  }

  render() {

    return (
      <ul 
        onClick={(e) => {
          console.log('parent');
          this.handleClick();
        }}
      >
        <li 
          onClick={(e) => {
            console.log('child');
            // prevent default? prevent propagation?
            this.handleClick();
          }}
        >
        </li>       
      </ul>
    )
  }
}

// => parent
// => child

Tôi có cùng một câu hỏi, stackoverflow.com/questions/44711549/...

Câu trả lời:


162

Tôi gặp vấn đề tương tự. Tôi thấy stopPropagation đã hoạt động. Tôi sẽ chia mục danh sách thành một thành phần riêng biệt, như vậy:

class List extends React.Component {
  handleClick = e => {
    // do something
  }

  render() {
    return (
      <ul onClick={this.handleClick}>
        <ListItem onClick={this.handleClick}>Item</ListItem> 
      </ul>
    )
  }
}

class ListItem extends React.Component {
  handleClick = e => {
    e.stopPropagation();  //  <------ Here is the magic
    this.props.onClick();
  }

  render() {
    return (
      <li onClick={this.handleClick}>
        {this.props.children}
      </li>       
    )
  }
}

Không nên this.props.handleClick()trong ListItemthành phần this.props.click?
blankface vào

3
Bất kỳ cách nào khác ngoại trừ stopPropogation?
Ali Sajid

Còn về thành phần có nguồn gốc như<Link>
samayo

điều gì sẽ xảy ra nếu bạn cần chuyển các tham số cho handleClick?
Manticore

Điều này không trả lời sai câu hỏi? OP yêu cầu ListItem để xử lý sự kiện. Giải pháp có Danh sách xử lý nó. (Trừ khi tôi đang hiểu nhầm điều gì đó.)
Thomas Jay Rush

57

React sử dụng ủy quyền sự kiện với một trình xử lý sự kiện duy nhất trên tài liệu cho các sự kiện có bong bóng, như 'nhấp chuột' trong ví dụ này, có nghĩa là không thể ngừng truyền; sự kiện thực sự đã được lan truyền vào thời điểm bạn tương tác với nó trong React. stopPropagation trên sự kiện tổng hợp của React là có thể vì React xử lý việc truyền các sự kiện tổng hợp trong nội bộ.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
}

siêu hữu ích .. stopPropagation () của riêng nó đã không hoạt động bình thường
pravin

1
Đây là những gì hiệu quả với tôi bởi vì tôi đang sử dụng document.addEventListener('click')kết hợp với phản ứngonClick
OtotheA

2
Đôi khi điều này có thể không hoạt động - ví dụ: nếu bạn đang sử dụng một thư viện như Material-UI (www.material-ui.com) hoặc gói các trình xử lý của bạn trong lệnh gọi lại của thành phần khác. Nhưng nếu mã của riêng bạn trực tiếp, thì tốt!
rob2d

1
@ rob2d, vậy làm thế nào để xử lý nó khi sử dụng Material-UI?
Italik

@Italik có một hàm mẫu ở trên; nhưng bạn cần gọi nó ngay lập tức như là điều đầu tiên trong lệnh gọi lại để nó không bị ảo hóa / nuốt. Ít nhất là AFAIK. Rất may là gần đây không phải chiến đấu với cái này.
rob2d

9

Theo thứ tự của các sự kiện DOM: CAPTURING và BUBBLING

Có hai giai đoạn cho cách sự kiện lan truyền. Chúng được gọi là "bắt""sủi bọt" .

               | |                                   / \
---------------| |-----------------   ---------------| |-----------------
| element1     | |                |   | element1     | |                |
|   -----------| |-----------     |   |   -----------| |-----------     |
|   |element2  \ /          |     |   |   |element2  | |          |     |
|   -------------------------     |   |   -------------------------     |
|        Event CAPTURING          |   |        Event BUBBLING           |
-----------------------------------   -----------------------------------

Giai đoạn bắt mồi xảy ra đầu tiên, sau đó là giai đoạn sủi bọt. Khi bạn đăng ký một sự kiện bằng api DOM thông thường, các sự kiện sẽ là một phần của giai đoạn sôi nổi theo mặc định, nhưng điều này có thể được chỉ định khi tạo sự kiện

// CAPTURING event
button.addEventListener('click', handleClick, true)

// BUBBLING events
button.addEventListener('click', handleClick, false)
button.addEventListener('click', handleClick)

Trong React, các sự kiện nổi bọt cũng là những gì bạn sử dụng theo mặc định.

// handleClick is a BUBBLING (synthetic) event
<button onClick={handleClick}></button>

// handleClick is a CAPTURING (synthetic) event
<button onClickCapture={handleClick}></button>

Hãy xem bên trong lệnh gọi lại handleClick (React) của chúng tôi:

function handleClick(e) {
  // This will prevent any synthetic events from firing after this one
  e.stopPropagation()
}
function handleClick(e) {
  // This will set e.defaultPrevented to true
  // (for all synthetic events firing after this one)
  e.preventDefault()  
}

Một giải pháp thay thế mà tôi chưa thấy đề cập ở đây

Nếu bạn gọi e.preventDefault () trong tất cả các sự kiện của mình, bạn có thể kiểm tra xem một sự kiện đã được xử lý chưa và ngăn sự kiện đó bị xử lý lại:

handleEvent(e) {
  if (e.defaultPrevented) return  // Exits here if event has been handled
  e.preventDefault()

  // Perform whatever you need to here.
}

Để biết sự khác biệt giữa sự kiện tổng hợp và sự kiện gốc, hãy xem tài liệu React: https://reactjs.org/docs/events.html


5

Điều này không phải là lý tưởng 100%, nhưng nếu quá khó để truyền lại propscho trẻ em -> thời trang trẻ em hoặc tạo Context.Provider/ Context.Consumer chỉ cho mục đích này) và bạn đang xử lý một thư viện khác có trình xử lý riêng mà nó chạy trước của bạn, bạn cũng có thể thử:

   function myHandler(e) { 
        e.persist(); 
        e.nativeEvent.stopImmediatePropagation();
        e.stopPropagation(); 
   }

Theo những gì tôi hiểu, event.persistphương pháp này ngăn một đối tượng ngay lập tức bị ném trở lại nhóm của React SyntheticEvent. Vì vậy, do đó, những thứ eventđã qua trong React thực sự không tồn tại vào thời điểm bạn tiếp cận nó! Điều này xảy ra ở các cháu do cách React xử lý nội bộ mọi thứ bằng cách kiểm tra đầu tiên phụ huynh xemSyntheticEvent trình xử lý của (đặc biệt nếu cha mẹ có một cuộc gọi lại).

Miễn là bạn không gọi persistthứ gì đó sẽ tạo ra bộ nhớ đáng kể để tiếp tục tạo các sự kiện chẳng hạn như onMouseMove(và bạn không tạo một số loại trò chơi Cookie Clicker như Grandma's Cookies), thì mọi chuyện sẽ hoàn toàn ổn!

Cũng lưu ý: thỉnh thoảng đọc xung quanh GitHub của họ, chúng ta nên để mắt đến các phiên bản React trong tương lai vì cuối cùng họ có thể giải quyết một số khó khăn với điều này vì dường như họ đang hướng tới việc tạo mã React gấp trong một trình biên dịch / chuyển tiếp.


1

Tôi gặp vấn đề khi event.stopPropagation()làm việc. Nếu bạn cũng vậy, hãy thử di chuyển nó lên đầu chức năng xử lý nhấp chuột của bạn, đó là những gì tôi cần làm để ngăn sự kiện nổi lên. Hàm ví dụ:

  toggleFilter(e) {
    e.stopPropagation(); // If moved to the end of the function, will not work
    let target = e.target;
    let i = 10; // Sanity breaker

    while(true) {
      if (--i === 0) { return; }
      if (target.classList.contains("filter")) {
        target.classList.toggle("active");
        break;
      }
      target = target.parentNode;
    }
  }

0

Bạn có thể tránh sự kiện sôi sục bằng cách kiểm tra mục tiêu của sự kiện.
Ví dụ: nếu bạn có đầu vào lồng vào phần tử div nơi bạn có trình xử lý cho sự kiện nhấp chuột và bạn không muốn xử lý nó, khi đầu vào được nhấp, bạn chỉ có thể chuyển event.targetvào trình xử lý của mình và kiểm tra xem trình xử lý có được thực thi dựa trên không thuộc tính của mục tiêu.
Ví dụ bạn có thể kiểm tra if (target.localName === "input") { return}.
Vì vậy, đó là một cách để "tránh" việc thực thi trình xử lý


-4

Cách mới để thực hiện việc này đơn giản hơn rất nhiều và sẽ giúp bạn tiết kiệm thời gian! Chỉ cần chuyển sự kiện vào trình xử lý nhấp chuột ban đầu và gọi preventDefault();.

clickHandler(e){
    e.preventDefault();
    //Your functionality here
}

3
-1; Tôi đã thử nghiệm điều này và nó không hoạt động để lan truyền nhấp chuột. AFAIK điều này sẽ hữu ích nếu bạn muốn một trình xử lý onClick cho các liên kết dừng chức năng liên kết cơ bản, nhưng không cho sự kiện sôi sục.
Joel Peltonen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.