ReactJS SyntheticEvent stopPropagation () ใช้ได้กับเหตุการณ์ React เท่านั้น?


126

ฉันกำลังพยายามใช้ event.stopPropagation () ภายในคอมโพเนนต์ ReactJS เพื่อหยุดเหตุการณ์การคลิกไม่ให้เดือดและทริกเกอร์เหตุการณ์การคลิกที่แนบมากับ JQuery ในโค้ดดั้งเดิม แต่ดูเหมือนว่า stopPropagation () ของ React จะหยุดการเผยแพร่ไปยังเหตุการณ์เท่านั้น ยังแนบอยู่ใน React และ stopPropagation () ของ JQuery ไม่หยุดการแพร่กระจายไปยังเหตุการณ์ที่แนบมากับ React

มีวิธีใดบ้างที่จะทำให้ stopPropagation () ทำงานในเหตุการณ์เหล่านี้ได้ ฉันเขียนJSFiddleง่ายๆเพื่อแสดงพฤติกรรมเหล่านี้:

/** @jsx React.DOM */
var Propagation = React.createClass({
    alert: function(){
        alert('React Alert');
    },
    stopPropagation: function(e){
        e.stopPropagation();
    },
    render: function(){
        return (
            <div>
                <div onClick={this.alert}>
                    <a href="#" onClick={this.stopPropagation}>React Stop Propagation on React Event</a>
                </div>
                <div className="alert">
                    <a href="#" onClick={this.stopPropagation}>React Stop Propagation on JQuery Event</a>
                </div>
                <div onClick={this.alert}>
                    <a href="#" className="stop-propagation">JQuery Stop Propagation on React Event</a>
                </div>
                <div className="alert">
                    <a href="#" className="stop-propagation">JQuery Stop Propagation on JQuery Event</a>
                </div>
            </div>
        );
    }
});

React.renderComponent(<Propagation />, document.body);

$(function(){    
    $(document).on('click', '.alert', function(e){
        alert('Jquery Alert');
    });

    $(document).on('click', '.stop-propagation', function(e){
        e.stopPropagation();
    });
});

9
ตัวฟังเหตุการณ์ที่แท้จริงของ React ยังอยู่ที่รากของเอกสารซึ่งหมายความว่าเหตุการณ์การคลิกได้กระจายไปที่รากแล้ว คุณสามารถใช้event.nativeEvent.stopImmediatePropagationเพื่อป้องกันไม่ให้ผู้ฟังเหตุการณ์อื่นเริ่มทำงานได้ แต่ไม่รับประกันลำดับการดำเนินการ
Ross Allen

3
ที่จริงแล้วstopImmediatePropagationผู้ฟังเหตุการณ์ที่อ้างสิทธิ์จะถูกเรียกตามลำดับที่พวกเขาถูกผูกมัด หาก React JS ของคุณเริ่มต้นก่อน jQuery ของคุณ (ตามที่อยู่ในซอของคุณ) การหยุดการเผยแพร่ทันทีจะได้ผล
Ross Allen

ตอบสนองหยุดไม่ให้ผู้ฟัง jQuery ถูกเรียก: jsfiddle.net/7LEDT/5เป็นไปไม่ได้ที่ jQuery จะป้องกันไม่ให้ React ถูกเรียกเนื่องจากผู้ฟัง jQuery ถูกผูกไว้ในซอของคุณในภายหลัง
Ross Allen

ทางเลือกหนึ่งคือการตั้งค่าตัวจัดการเหตุการณ์ jQuery ของคุณเองcomponentDidMountแต่อาจรบกวนตัวจัดการเหตุการณ์ React อื่น ๆ ด้วยวิธีที่ไม่คาดคิด
Douglas

ฉันตระหนักว่าตัวอย่างที่สองเกี่ยวกับ.stop-propagationจำเป็นจะไม่ได้ผล ตัวอย่างของคุณใช้การมอบหมายเหตุการณ์ แต่กำลังพยายามหยุดการเผยแพร่ที่องค์ประกอบ ผู้ฟังต้องผูกพันกับองค์ประกอบนั้นเอง: $('.stop-propagation').on('click', function(e) { e.stopPropagation(); });. ซอนี้ป้องกันการแพร่กระจายทั้งหมดอย่างที่คุณพยายาม: jsfiddle.net/7LEDT/6
Ross Allen

คำตอบ:


165

React ใช้การมอบหมายเหตุการณ์กับผู้ฟังเหตุการณ์เดียวdocumentสำหรับเหตุการณ์ที่เกิดฟองเช่น "คลิก" ในตัวอย่างนี้ซึ่งหมายความว่าไม่สามารถหยุดการเผยแพร่ได้ เหตุการณ์จริงได้แพร่กระจายไปแล้วตามเวลาที่คุณโต้ตอบใน React stopPropagationเหตุการณ์สังเคราะห์ของ React เป็นไปได้เนื่องจาก React จัดการการแพร่กระจายของเหตุการณ์สังเคราะห์ภายใน

การทำงานJSFiddleกับการแก้ไขจากด้านล่าง

ตอบสนองหยุดการแพร่กระจายบนเหตุการณ์ jQuery

ใช้Event.stopImmediatePropagationเพื่อป้องกันไม่ให้ผู้ฟัง (jQuery ในกรณีนี้) อื่น ๆ ของคุณถูกเรียกใช้ ได้รับการสนับสนุนใน IE9 + และเบราว์เซอร์สมัยใหม่

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
},
  • Caveat: ผู้ฟังถูกเรียกตามลำดับที่พวกเขาถูกผูกไว้ ต้องเริ่มต้นการตอบสนองก่อนโค้ดอื่น ๆ (jQuery ที่นี่) เพื่อให้สามารถใช้งานได้

jQuery Stop Propagation on React Event

รหัส jQuery ของคุณใช้การมอบหมายเหตุการณ์เช่นกันซึ่งหมายความว่าการโทรstopPropagationในตัวจัดการจะไม่หยุดอะไรเลย เหตุการณ์ได้แพร่กระจายไปdocumentแล้วและผู้ฟังของ React จะถูกทริกเกอร์

// Listener bound to `document`, event delegation
$(document).on('click', '.stop-propagation', function(e){
    e.stopPropagation();
});

เพื่อป้องกันการแพร่กระจายเกินองค์ประกอบผู้ฟังต้องถูกผูกไว้กับองค์ประกอบนั้นเอง:

// Listener bound to `.stop-propagation`, no delegation
$('.stop-propagation').on('click', function(e){
    e.stopPropagation();
});

แก้ไข (2016/01/14):ชี้แจงว่าการมอบหมายจำเป็นต้องใช้สำหรับเหตุการณ์ที่เกิดขึ้นเท่านั้น สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการจัดการเหตุการณ์ตอบสนองของแหล่งที่มามีความเห็นเชิงพรรณนา: ReactBrowserEventEmitter.js


@ssorellen: การชี้แจง: React ผูกตัวฟังเหตุการณ์ไว้บนdocumentหรือองค์ประกอบการตอบสนองรากหรือไม่? หน้าเชื่อมโยงเพียงแค่บอกว่า "ที่ระดับด้านบน" ซึ่งผมใช้เวลาที่จะหมายความว่ามันไม่document ( แต่ฉันไม่สามารถคิดออกว่านี้มีความเกี่ยวข้องแม้)
user128216

2
@FighterJet ขึ้นอยู่กับประเภทเหตุการณ์ สำหรับเหตุการณ์ที่เกิดฟองจะใช้การมอบหมายระดับบนสุด สำหรับเหตุการณ์ที่ไม่เกิดฟอง React จำเป็นต้องรับฟังโหนด DOM ต่างๆ มีคำอธิบายอย่างละเอียดมากขึ้นใน ReactBrowserEventEmitter.js: github.com/facebook/react/blob/…
Ross Allen

ขออภัยสำหรับการลดคะแนนชั่วคราว ฉันต้องการมันสำหรับรายงานข้อผิดพลาดและฉันได้แทนที่ด้วยการโหวตเพิ่ม :)
Glorfindel

ลองดูคำตอบของ Jim ซึ่งแนะนำให้เพิ่ม global click listener windowแทนdocument: stackoverflow.com/a/52879137/438273
jsejcksn

13

มูลค่า noting (จากปัญหานี้ ) ว่าถ้าคุณติดกิจกรรมเพื่อdocument, e.stopPropagation()ไม่ได้ไปช่วยเหลือ วิธีแก้ปัญหาชั่วคราวคุณสามารถใช้window.addEventListener()แทนจากdocument.addEventListenerนั้นevent.stopPropagation()จะหยุดเหตุการณ์ไม่ให้แพร่กระจายไปยังหน้าต่าง


ขอบคุณมาก! นี่คือสิ่งที่ฉันมีและวิธีนี้ใช้ได้ผล
Anh Tran

10

ยังคงเป็นช่วงเวลาหนึ่งที่น่าสนใจ:

ev.preventDefault()
ev.stopPropagation();
ev.nativeEvent.stopImmediatePropagation();

ใช้โครงสร้างนี้หากฟังก์ชันของคุณถูกรวมไว้ด้วยแท็ก


1
event.nativeEventเป็นคำตอบที่ถูกต้อง ฉันสามารถใช้composedPath()มันได้:event.nativeEvent.composedPath()
jimmont

คุณหมายถึงwrapped by tagอะไร? คุณช่วยยกตัวอย่างได้ไหม
JimmyLv

@JimmyLv ฉันหมายถึง <div onClick=(firstHandler)> <div onClick=(secHandler)>active text</div> </div>
Roman

7

ฉันสามารถแก้ไขปัญหานี้ได้โดยเพิ่มสิ่งต่อไปนี้ในส่วนประกอบของฉัน:

componentDidMount() {
  ReactDOM.findDOMNode(this).addEventListener('click', (event) => {
    event.stopPropagation();
  }, false);
}

2
สิ่งนี้จะหยุด SynteticEvents โดยสิ้นเชิงเนื่องจาก React ใช้การมอบหมายเหตุการณ์กับผู้ฟังเหตุการณ์เดียวในเอกสารสำหรับเหตุการณ์ที่เกิดฟอง
Vakhtang

5

เมื่อวานนี้ฉันพบปัญหานี้ดังนั้นฉันจึงสร้างโซลูชันที่เป็นมิตรกับปฏิกิริยา

ตรวจสอบการตอบสนองพื้นเมือง-ฟัง มันทำงานได้ดีมากจนถึงตอนนี้ คำติชมชื่นชม


5
react-native-listenerการใช้งานfindDomNodeซึ่งจะเลิกใช้github.com/yannickcr/eslint-plugin-react/issues/…
Bohdan Lyzanets

ลงคะแนนเนื่องจากคำตอบไม่ใช่สถานที่ที่เหมาะสมในการทำการตลาดหรือส่งมอบโซลูชันภายนอกซึ่งไม่ช่วยเสริมคำตอบที่สมบูรณ์
jimmont

2

จากปฏิกิริยาเอกสารการจัดการเหตุการณ์ดังต่อไปนี้จะเรียกโดยเหตุการณ์ในช่วงเดือดปุดหากต้องการลงทะเบียนตัวจัดการเหตุการณ์สำหรับขั้นตอนการจับภาพจับภาพผนวก (เน้นเพิ่ม)

หากคุณมีฟังเหตุการณ์การคลิกในรหัสการตอบสนองของคุณและคุณไม่ต้องการให้ฟองขึ้นผมคิดว่าสิ่งที่คุณต้องการจะทำคือการใช้แทนonClickCapture onClickจากนั้นคุณจะส่งต่อเหตุการณ์ไปยังตัวจัดการและทำevent.nativeEvent.stopPropagation()เพื่อป้องกันไม่ให้เหตุการณ์เนทีฟเดือดพล่านไปจนถึงผู้ฟังเหตุการณ์วานิลลา JS (หรืออะไรก็ตามที่ไม่ตอบสนอง)


1

อัปเดต: ตอนนี้คุณทำได้แล้ว <Elem onClick={ proxy => proxy.stopPropagation() } />


1
ดูคำตอบของ @ RossAllen สิ่งนี้จะไม่ป้องกันการเผยแพร่ไปยังผู้ฟัง DOM ดั้งเดิมของบรรพบุรุษ (ซึ่ง jQuery ใช้)
Ben Mosher

นี่เป็นวิธีที่ถูกต้องถ้าคุณใช้ส่วนประกอบปฏิกิริยา การเล่นไฮแจ็คไม่ใช่ความคิดที่ดี
Eric Hodonsky

หากมีสิ่งอื่นล้มเหลวนั่นเป็นเพราะคุณทำสิ่งอื่นผิด
Eric Hodonsky

1
ฉันยอมรับว่านั่นเป็นวิธีที่ถูกต้องหากผู้ฟังทั้งหมดของคุณแนบผ่าน React แต่นั่นไม่ใช่คำถามที่นี่
Ben Mosher

นั่นเป็นเพียงตัวฉันเอง ... ฉันจะสนับสนุนให้ใช้วิธีที่ถูกต้องอย่างถูกต้องเสมอแทนที่จะชักนำใครบางคนให้หลงทางด้วยวิธีการที่อาจทำให้พวกเขาเศร้าโศกในอนาคตเพราะพวกเขามี 'ทางออก' สำหรับปัญหา
Eric Hodonsky

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.