अभिक्रिया js में onchange घटना को ट्रिगर करने का सबसे अच्छा तरीका क्या है


112

हम एक क्लाइंट-साइड ऐप बनाने के लिए Backbone + ReactJS बंडल का उपयोग करते हैं। भारी रूप से कुख्यात पर भरोसा करते हुए valueLinkहम अपने स्वयं के आवरण के माध्यम से सीधे मॉडल पर मूल्यों का प्रचार करते हैं जो दो तरह से बाध्यकारी के लिए ReactJS इंटरफ़ेस का समर्थन करता है।

अब हमें समस्या का सामना करना पड़ा:

हमारे पास jquery.mask.jsप्लगइन है, जो प्रोग्रामेटिक रूप से इनपुट मूल्य को प्रारूपित करता है, इसलिए यह रिएक्ट घटनाओं को आग नहीं देता है। यह सब उस स्थिति में होता है जब उपयोगकर्ता उपयोगकर्ता इनपुट से अनफ़ॉर्मेट मान प्राप्त करता है और प्लगइन से स्वरूपित करता है

ऐसा लगता है कि रिएक्ट में ब्राउज़र के आधार पर बहुत सारे इवेंट हैंडलिंग रणनीतियाँ हैं। क्या विशेष DOM तत्व के लिए परिवर्तन घटना को ट्रिगर करने का कोई सामान्य तरीका है ताकि React इसे सुने?


जवाबों:


174

प्रतिक्रिया के लिए 16 और प्रतिक्रिया> = 15.6

सेटर .value=काम नहीं कर रहा है जैसा हम चाहते थे क्योंकि रिएक्ट लाइब्रेरी इनपुट वैल्यू सेटर को ओवरराइड करती है लेकिन हम फंक्शन को सीधे inputसंदर्भ के अनुसार कह सकते हैं ।

var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, 'react 16 value');

var ev2 = new Event('input', { bubbles: true});
input.dispatchEvent(ev2);

पाठ क्षेत्र तत्व के लिए आप का उपयोग करना चाहिए prototypeके HTMLTextAreaElementवर्ग।

नया कोडपैन उदाहरण

सभी इस योगदानकर्ता और उसके समाधान का श्रेय देते हैं

केवल प्रतिक्रिया के लिए आउटडेटेड उत्तर <= 15.5

साथ react-dom ^15.6.0आप उपयोग कर सकते simulatedघटना वस्तु पर झंडा घटना के माध्यम से पारित करने के लिए

var ev = new Event('input', { bubbles: true});
ev.simulated = true;
element.value = 'Something new';
element.dispatchEvent(ev);

मैंने एक उदाहरण के साथ एक कोडपेन बनाया

यह समझने के लिए कि नए ध्वज की आवश्यकता क्यों है, मुझे यह टिप्पणी बहुत उपयोगी लगी:

रीएक्ट में इनपुट लॉजिक अब बदलाव की घटनाओं को घटा देता है ताकि वे प्रति मूल्य एक से अधिक बार फायर न करें। यह दोनों ब्राउज़र onChange / onInput घटनाओं के साथ-साथ DOM नोड वैल्यू प्रोप पर सेट करता है (जब आप जावास्क्रिप्ट के माध्यम से मूल्य अपडेट करते हैं)। इसका अर्थ का साइड इफेक्ट है कि यदि आप इनपुट के वैल्यू को मैन्युअल रूप से input.value = 'foo' से अपडेट करते हैं तो एक ChangeEvent को {target: इनपुट} के साथ भेज दें। प्रतिक्रिया सेट और ईवेंट दोनों को पंजीकृत करेगी, देखें कि यह मान अभी भी `foo है ', इसे एक डुप्लिकेट घटना पर विचार करें और इसे निगल लें।

यह सामान्य मामलों में ठीक काम करता है क्योंकि "वास्तविक" ब्राउज़र द्वारा शुरू की गई घटना एलीमेंट पर सेट नहीं होती है। आप चुपके से एक झंडे के साथ ट्रिगर होने वाली घटना को टैग करके इस तर्क से बच सकते हैं और प्रतिक्रिया हमेशा घटना को आग लगा देगी। https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128


1
धन्यवाद, इसने प्रतिक्रिया-डोम ^ 15.6.0 से काम किया। लेकिन ऐसा लगता है कि रिएक्ट 16.0 के बाद यह काम करना बंद कर दिया है। परिवर्तन की घटनाओं को गति देने के लिए नकली ध्वज का उपयोग करने के विकल्प पर कोई विचार?
क्वर्टी

विश्वास है कि यहाँ एक बिंदु है जो संकेत देगा: reactjs.org/blog/2017/09/26/react-v16.0.html#breaking-changes यह क्या हो सकता है किसी भी विचार?
क्वर्टी

@ क्वर्टी मैंने अपना जवाब अपडेट किया, हो सकता है कि यह आपके लिए काम करे
ग्रिन

और क्या बटन पर क्लिक करें? इस तरह के आयोजन को शुरू करने के लिए क्या किया जाना चाहिए?
बेंडर

@ आप बस मूल क्लिक विधि को तत्व पर कहते हैं
Grin

63

कम से कम पाठ इनपुट पर, ऐसा प्रतीत होता है कि onChangeइनपुट घटनाओं के लिए सुन रहा है:

var event = new Event('input', { bubbles: true });
element.dispatchEvent(event);

ब्राउज़र संस्करण पर निर्भर करता है। IE8 इनपुट घटना का समर्थन नहीं करता है। और ie9 टेक्स्ट ईवेंट को फायर नहीं करता है जब आप टेक्स्ट बॉक्स से वर्ण हटाते हैं। developer.mozilla.org/en-US/docs/Web/Events/input
wallice


1
IE8 अब React द्वारा समर्थित नहीं है । IE9 के लिए, आप कुछ का उपयोग करने में सक्षम हो सकते हैं var event = document.createEvent('CustomEvent'); event.initCustomEvent('input', true, false, { });, लेकिन मेरे पास IE9 VM नहीं है।
माइकल

@ मिचेल मैं IE11 पर आपके कोड की कोशिश कर रहा हूं और यह रिएक्टज इनपुट क्षेत्रों पर काम नहीं कर रहा है। यह सामान्य HTML इनपुट पर काम करता है। यह एज पर भी काम करता है। var evt = document.createEvent('CustomEvent'); evt.initCustomEvent('input', true, false, { }); document.getElementById('description').value = 'I changed description'; document.getElementById('description').dispatchEvent(evt);
बोडोस्को

19

मुझे पता है कि यह उत्तर थोड़ी देर से आता है लेकिन मुझे हाल ही में इसी तरह की समस्या का सामना करना पड़ा। मैं एक नेस्टेड घटक पर एक घटना को ट्रिगर करना चाहता था। मेरे पास रेडियो और चेक बॉक्स प्रकार विजेट्स के साथ एक सूची थी (वे divs थे जो चेकबॉक्स और / या रेडियो बटन की तरह व्यवहार किए गए थे) और आवेदन में किसी अन्य स्थान पर, अगर किसी ने टूलबॉक्स बंद कर दिया, तो मुझे एक को अनचेक करना होगा।

मुझे एक बहुत ही सरल उपाय मिला, यकीन नहीं होता कि यह सबसे अच्छा अभ्यास है लेकिन यह काम करता है।

var event = new MouseEvent('click', {
 'view': window, 
 'bubbles': true, 
 'cancelable': false
});
var node = document.getElementById('nodeMyComponentsEventIsConnectedTo');
node.dispatchEvent(event);

इसने डोमनोड पर क्लिक इवेंट को ट्रिगर किया और मेरे हैंडलर को रिएक्शन के जरिए अटैच किया गया था, इसलिए यह ऐसा व्यवहार करता है जैसे मैं उम्मीद करता हूं कि अगर कोई तत्व पर क्लिक करेगा। मैंने onChange का परीक्षण नहीं किया है, लेकिन यह काम करना चाहिए, और यह सुनिश्चित नहीं करना चाहिए कि यह IE के पुराने संस्करणों में कैसे उचित होगा लेकिन मुझे विश्वास है कि माउसएवेंट कम से कम IE9 और ऊपर में समर्थित है।

मैं अपने विशेष उपयोग के मामले के लिए अंततः इससे दूर हो गया क्योंकि मेरा घटक बहुत छोटा था (मेरे आवेदन का केवल एक हिस्सा इस्तेमाल किया गया प्रतिक्रिया के बाद से मैं अभी भी इसे सीख रहा हूं) और मैं डोम नोड्स के संदर्भ प्राप्त किए बिना एक ही चीज़ को प्राप्त कर सकता था।

अपडेट करें:

जैसा कि दूसरों ने टिप्पणियों में कहा है, this.refs.refnameडोम नोड का संदर्भ प्राप्त करने के लिए उपयोग करना बेहतर है । इस मामले में, refname आपके द्वारा अपने घटक से जुड़ी रेफरी है <MyComponent ref='refname' />


1
आईडी के बजाय, आप React.findDOMNodeफ़ंक्शन का उपयोग कर सकते हैं । goo.gl/RqccrA
m93a

2
> आईडी के बजाय, आप React.findDOMNode फ़ंक्शन का उपयोग कर सकते हैं। या refफिर अपने तत्व में जोड़ें, फिर उपयोग करेंthis.ref.refName.dispatchEvent
सिल्कएक्मिन

फ्रेमवर्क के बाद से सबसे अधिक संभावना बदल गई है, इसका यह .ref s .refname
nclord

1
स्ट्रिंग रेफरी को अब विरासत माना जाता है और भविष्य में पदावनत किया जाएगा। डोम नोड का ट्रैक रखने के लिए कॉलबैक रेफरी पसंदीदा तरीका है।
dzv3

9

आप ReactTestUtils का उपयोग करके घटनाओं का अनुकरण कर सकते हैं लेकिन यह इकाई परीक्षण के लिए डिज़ाइन किया गया है।

मैं इस मामले के लिए valueLink का उपयोग नहीं करने की सलाह देता हूं और बस प्लगइन द्वारा निकाल दी गई घटनाओं को बदलने के लिए सुन रहा हूं और प्रतिक्रिया में इनपुट की स्थिति को अपडेट कर रहा हूं। दो-तरफ़ा बाध्यकारी बर्तन किसी भी चीज़ की तुलना में डेमो के रूप में अधिक हैं; वे केवल इस तथ्य पर जोर देने के लिए जोड़ में शामिल हैं कि शुद्ध दो-तरफा बाध्यकारी अधिकांश अनुप्रयोगों के लिए उपयुक्त नहीं है और आपको आमतौर पर अपने ऐप में इंटरैक्शन का वर्णन करने के लिए अधिक एप्लिकेशन लॉजिक की आवश्यकता होती है।


मुझे डर था कि जवाब कुछ ऐसा होगा (और समस्या यह है कि रिएक्ट भेजने / प्राप्त करने के साथ बहुत सख्त है। वर्कफ़्लो को विशेष रूप से ऑनचेंज हैंडलर को निर्दिष्ट करना होगा जो उपयोगकर्ता इनपुट पर प्रतिक्रिया करने में सक्षम हो, अनियंत्रित राज्य को छोड़कर कोई अन्य तरीका नहीं है जो कि तरह है मेरे लिए बायलरप्लेट का ओवर। मेरे मामले में, मुझे इस हैंडलर को केवल नियमों का पालन करने के लिए घोषित करना चाहिए, जबकि प्रासंगिक इनपुट
ऑन

1
और ... यदि आप ReactTestUtils का उपयोग करना चाहते हैं ...ReactTestUtils.Simulate.change(ReactDOM.findDOMNode(this.fieldRef))
Colllin

8

ग्रिन / डैन अब्रामोव के जवाब पर विस्तार करते हुए, यह कई इनपुट प्रकारों में काम करता है। प्रतिक्रिया में परीक्षण> = 15.5

const inputTypes = [
    window.HTMLInputElement,
    window.HTMLSelectElement,
    window.HTMLTextAreaElement,
];

export const triggerInputChange = (node, value = '') => {

    // only process the change on elements we know have a value setter in their constructor
    if ( inputTypes.indexOf(node.__proto__.constructor) >-1 ) {

        const setValue = Object.getOwnPropertyDescriptor(node.__proto__, 'value').set;
        const event = new Event('input', { bubbles: true });

        setValue.call(node, value);
        node.dispatchEvent(event);

    }

};

5
चुनिंदा तत्वों के लिए काम नहीं करता है। आपको ईवेंट के लिए 'इनपुट' के बजाय 'परिवर्तन' की आवश्यकता है।
स्टाइल

3

मनमाने तत्वों पर परिवर्तन घटनाओं को ट्रिगर करना घटकों के बीच निर्भरता पैदा करता है, जिनके बारे में तर्क करना मुश्किल है। रिएक्ट के वन-वे डेटा प्रवाह के साथ रहना बेहतर है।

रिएक्ट के परिवर्तन की घटना को ट्रिगर करने के लिए कोई सरल स्निपेट नहीं है। ChangeEventPlugin.js में तर्क लागू किया गया है और विभिन्न इनपुट प्रकारों और ब्राउज़रों के लिए अलग-अलग कोड शाखाएं हैं। इसके अलावा, कार्यान्वयन विवरण प्रतिक्रिया के संस्करणों में भिन्न होता है।

मैंने प्रतिक्रिया-ट्रिगर-परिवर्तन का निर्माण किया है जो बात करता है, लेकिन इसका उपयोग परीक्षण के लिए किया जाना है, न कि उत्पादन निर्भरता के रूप में:

let node;
ReactDOM.render(
  <input
    onChange={() => console.log('changed')}
    ref={(input) => { node = input; }}
  />,
  mountNode
);

reactTriggerChange(node); // 'changed' is logged

CodePen


1

चूंकि हम एक onchange घटना को संभालने के लिए फ़ंक्शन का उपयोग करते हैं, इसलिए हम इसे इस तरह कर सकते हैं:

class Form extends Component {
 constructor(props) {
  super(props);
  this.handlePasswordChange = this.handlePasswordChange.bind(this);
  this.state = { password: '' }
 }

 aForceChange() {
  // something happened and a passwordChange
  // needs to be triggered!!

  // simple, just call the onChange handler
  this.handlePasswordChange('my password');
 }

 handlePasswordChange(value) {
 // do something
 }

 render() {
  return (
   <input type="text" value={this.state.password} onChange={changeEvent => this.handlePasswordChange(changeEvent.target.value)} />
  );
 }
}

1

मुझे यह रिएक्ट के गितुब मुद्दों पर मिला: एक आकर्षण की तरह काम करता है (v15.6.2)

यहां बताया गया है कि मैंने टेक्स्ट इनपुट पर कैसे लागू किया:

changeInputValue = newValue => {

    const e = new Event('input', { bubbles: true })
    const input = document.querySelector('input[name=' + this.props.name + ']')
    console.log('input', input)
    this.setNativeValue(input, newValue)
    input.dispatchEvent(e)
  }

  setNativeValue (element, value) {
    const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set
    const prototype = Object.getPrototypeOf(element)
    const prototypeValueSetter = Object.getOwnPropertyDescriptor(
      prototype,
      'value'
    ).set

    if (valueSetter && valueSetter !== prototypeValueSetter) {
      prototypeValueSetter.call(element, value)
    } else {
      valueSetter.call(element, value)
    }
  }

यह अगर पाठ बॉक्स मूल्य मूल्य आप setNativeValue करने से गुजर रहे हैं के रूप में ही है काम नहीं करता है
अरुण

0

के लिए HTMLSelectElement, यानी<select>

var element = document.getElementById("element-id");
var trigger = Object.getOwnPropertyDescriptor(
  window.HTMLSelectElement.prototype,
  "value"
).set;
trigger.call(element, 4); // 4 is the select option's value we want to set
var event = new Event("change", { bubbles: true });
element.dispatchEvent(event);

-1

यदि आप Backbone और React का उपयोग कर रहे हैं, तो मैं निम्नलिखित में से एक की सिफारिश करूंगा,

वे दोनों प्रतिक्रिया के विचारों के साथ बैकबोन मॉडल और संग्रह को एकीकृत करने में मदद करते हैं। आप बैकबोन घटनाओं का उपयोग वैसे ही कर सकते हैं जैसे आप बैकबोन विचारों के साथ करते हैं। मैंने दोनों में डब किया है और एक अंतर को छोड़कर एक मिक्सिन और अन्य परिवर्तनों React.createClassको छोड़कर बहुत अंतर नहीं देखा है React.createBackboneClass


कृपया, प्रतिक्रिया और रीढ़ की हड्डी के बीच उन "घटना-संचालित" पुलों के साथ सावधान रहें पहला प्लगइन घटक माउंट स्तर की उपेक्षा करता है। यह एक गलती है। दूसरा बहुत अधिक ताकतवर और दिमाग पर निर्भर करता है कि केवल एक मॉडल को एक घटक के लिए सौंपा जा सकता है, जो सामान्य स्थिति नहीं है। अधिक से अधिक, यदि यू यूआई मॉडल को जटिल यूआई के साथ प्रतिक्रिया घटकों के साथ साझा करते हैं और बेकार अपडेट घटनाओं से अनसब्सक्राइब करना भूल गए हैं जो जबरदस्ती का कारण बनते हैं, तो आप पुनरावर्ती प्रतिपादन में गिर सकते हैं, इसके बारे में जागरूक रहें।
चारदीवारी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.