Cách tốt nhất để kích hoạt sự kiện thay đổi trong phản ứng js là gì


112

Chúng tôi sử dụng gói Backbone + ReactJS để xây dựng ứng dụng phía máy khách. Dựa nhiều vào tai tiếng, valueLinkchúng tôi truyền các giá trị trực tiếp đến mô hình thông qua trình bao bọc riêng hỗ trợ giao diện ReactJS cho liên kết hai chiều.

Bây giờ chúng tôi phải đối mặt với vấn đề:

Chúng tôi có jquery.mask.jsplugin định dạng giá trị đầu vào theo chương trình do đó nó không kích hoạt các sự kiện React. Tất cả điều này dẫn đến tình huống khi mô hình nhận các giá trị chưa được định dạng từ đầu vào của người dùng và bỏ lỡ các giá trị được định dạng từ plugin.

Có vẻ như React có rất nhiều chiến lược xử lý sự kiện tùy thuộc vào trình duyệt. Có cách phổ biến nào để kích hoạt sự kiện thay đổi cho phần tử DOM cụ thể để React nghe thấy nó không?


Câu trả lời:


174

Đối với React 16 và React> = 15,6

Setter .value=không hoạt động như chúng ta mong muốn vì thư viện React ghi đè setter giá trị đầu vào nhưng chúng ta có thể gọi hàm trực tiếp trên inputngữ cảnh as.

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);

Đối với yếu tố textarea bạn nên sử dụng prototypecác HTMLTextAreaElementlớp.

Ví dụ về codepen mới .

Tất cả các khoản tín dụng cho người đóng góp nàygiải pháp của anh ấy

Câu trả lời lỗi thời chỉ dành cho React <= 15,5

Với react-dom ^15.6.0bạn có thể sử dụng simulatedcờ trên đối tượng sự kiện để sự kiện đi qua

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

Tôi đã tạo một codepen với một ví dụ

Để hiểu lý do tại sao cần có cờ mới, tôi thấy nhận xét này rất hữu ích:

Logic đầu vào trong React hiện đã loại trừ các sự kiện thay đổi của nó để chúng không kích hoạt nhiều hơn một lần cho mỗi giá trị. Nó lắng nghe cả các sự kiện onChange / onInput của trình duyệt cũng như thiết lập giá trị nút DOM (khi bạn cập nhật giá trị thông qua javascript). Điều này có tác dụng phụ là nếu bạn cập nhật giá trị của đầu vào theo cách thủ công input.value = 'foo' sau đó gửi một ChangeEvent với {target: input} thì React sẽ đăng ký cả tập hợp và sự kiện, hãy xem giá trị của nó vẫn là '' foo ', hãy coi đó là một sự kiện trùng lặp và nuốt chửng nó.

Điều này hoạt động tốt trong các trường hợp bình thường vì sự kiện khởi tạo trình duyệt "thực" không kích hoạt các tập hợp trên element.value. Bạn có thể bảo đảm bí mật thoát khỏi logic này bằng cách gắn thẻ sự kiện bạn kích hoạt bằng một cờ mô phỏng và phản ứng sẽ luôn kích hoạt sự kiện. https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128


1
Cảm ơn, điều này đã hoạt động từ react-dom ^ 15.6.0. Nhưng có vẻ như bài React 16.0 này đã ngừng hoạt động. Bất kỳ ý tưởng nào về giải pháp thay thế cho việc sử dụng cờ mô phỏng để kích hoạt các sự kiện thay đổi?
Qwerty

Tin rằng có một điểm ở đây sẽ đưa ra gợi ý: reactjs.org/blog/2017/09/26/react-v16.0.html#break-changes Bạn có biết nó có thể là gì không?
Qwerty

@Qwerty Tôi đã cập nhật câu trả lời của mình, có thể nó sẽ phù hợp với bạn
Grin

và nút abou nào onClick? những gì sẽ được thực hiện để kích hoạt loại sự kiện này?
Bender

@Bender bạn chỉ cần gọi phương thức nhấp chuột gốc trên phần tử
Grin

63

Ít nhất trên các đầu vào văn bản, nó dường như onChangeđang lắng nghe các sự kiện đầu vào:

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

Phụ thuộc vào phiên bản trình duyệt. IE8 không hỗ trợ sự kiện đầu vào. Và ie9 không kích hoạt sự kiện đầu vào khi bạn xóa các ký tự khỏi hộp văn bản. developer.mozilla.org/en-US/docs/Web/Events/input
wallice


1
IE8 không còn được hỗ trợ bởi React. Đối với IE9, bạn có thể sử dụng một cái gì đó như var event = document.createEvent('CustomEvent'); event.initCustomEvent('input', true, false, { });, nhưng tôi không có IE9 VM tiện dụng.
Michael

@Michael Tôi đang thử mã của bạn trên IE11 và mã này không hoạt động trên các trường nhập reactjs. Nó hoạt động trên các đầu vào HTML thông thường. Nó cũng hoạt động trên Edge. var evt = document.createEvent('CustomEvent'); evt.initCustomEvent('input', true, false, { }); document.getElementById('description').value = 'I changed description'; document.getElementById('description').dispatchEvent(evt);
Bodosko

19

Tôi biết câu trả lời này đến hơi muộn nhưng gần đây tôi đã đối mặt với một vấn đề tương tự. Tôi muốn kích hoạt một sự kiện trên một thành phần lồng nhau. Tôi có một danh sách với các widget loại hộp kiểm và radio (chúng là các div hoạt động giống như các hộp kiểm và / hoặc nút radio) và ở một số vị trí khác trong ứng dụng, nếu ai đó đã đóng một hộp công cụ, tôi cần bỏ chọn một hộp.

Tôi đã tìm thấy một giải pháp khá đơn giản, không chắc đây có phải là phương pháp hay nhất hay không nhưng nó hoạt động.

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

Điều này đã kích hoạt sự kiện nhấp chuột trên domNode và trình xử lý của tôi được đính kèm qua phản ứng thực sự được gọi vì vậy nó hoạt động như tôi mong đợi nếu ai đó nhấp vào phần tử. Tôi chưa thử nghiệm onChange nhưng nó sẽ hoạt động và không chắc điều này sẽ công bằng như thế nào trong các phiên bản IE thực sự cũ nhưng tôi tin rằng MouseEvent được hỗ trợ trong ít nhất IE9 trở lên.

Cuối cùng tôi đã bỏ điều này cho trường hợp sử dụng cụ thể của mình vì thành phần của tôi rất nhỏ (chỉ một phần ứng dụng của tôi được sử dụng react vì tôi vẫn đang học nó) và tôi có thể đạt được điều tương tự theo cách khác mà không cần tham chiếu đến các nút dom.

CẬP NHẬT:

Như những người khác đã nêu trong các nhận xét, tốt hơn là sử dụng this.refs.refnameđể lấy tham chiếu đến nút dom. Trong trường hợp này, refname là ref mà bạn đã gắn vào thành phần của mình <MyComponent ref='refname' />.


1
Thay vì ID, bạn có thể sử dụng React.findDOMNodehàm. goo.gl/RqccrA
m93a

2
> Thay vì ID, bạn có thể sử dụng hàm React.findDOMNode. Hoặc thêm refvào yếu tố của bạn sau đó sử dụngthis.ref.refName.dispatchEvent
silkAdmin

Khung đã rất có thể đã thay đổi kể từ đó, nó this.ref s .refname
nclord

1
Các tham chiếu chuỗi được coi là kế thừa hiện tại và sẽ không được dùng nữa trong tương lai. Giới thiệu gọi lại là phương pháp ưa thích để theo dõi nút DOM.
dzv3

9

Bạn có thể mô phỏng các sự kiện bằng ReactTestUtils nhưng điều đó được thiết kế để kiểm tra đơn vị.

Tôi khuyên bạn không nên sử dụng valueLink cho trường hợp này và chỉ cần lắng nghe các sự kiện thay đổi do plugin kích hoạt và cập nhật trạng thái của đầu vào để phản hồi. Ràng buộc hai chiều giống như một bản demo hơn bất cứ thứ gì khác; chúng được đưa vào các phần bổ trợ chỉ để nhấn mạnh thực tế là liên kết hai chiều thuần túy không thích hợp cho hầu hết các ứng dụng và bạn thường cần thêm logic ứng dụng để mô tả các tương tác trong ứng dụng của mình.


Tôi sợ rằng câu trả lời sẽ giống như vậy (. Vấn đề là React quá nghiêm ngặt với quy trình gửi / nhận. Cụ thể là người ta phải chỉ định trình xử lý onChange để có thể phản ứng khi người dùng nhập vào, không có cách nào khác ngoại trừ trạng thái không kiểm soát được. over boilerplate cho tôi.Trong trường hợp của tôi, tôi chỉ nên khai báo trình xử lý này để tuân theo các quy tắc, trong khi đầu vào có liên quan sẽ đến từ sự kiện onchange được kích hoạt bởi jquery. React thực sự thiếu ý tưởng về việc mở rộng điểm cuối gửi / nhận với mã do người dùng khai báo
wallice

1
Và ... trong trường hợp bạn muốn sử dụng ReactTestUtils ...ReactTestUtils.Simulate.change(ReactDOM.findDOMNode(this.fieldRef))
colllin

8

Mở rộng câu trả lời từ Grin / Dan Abramov, điều này hoạt động trên nhiều loại đầu vào. Đã kiểm tra trong React> = 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
Không hoạt động cho các phần tử được chọn. Bạn cần 'thay đổi' thay vì 'đầu vào' cho sự kiện.
styks

3

Việc kích hoạt các sự kiện thay đổi trên các phần tử tùy ý tạo ra sự phụ thuộc giữa các thành phần mà khó có thể lý giải được. Tốt hơn là bạn nên tuân theo luồng dữ liệu một chiều của React.

Không có đoạn mã đơn giản nào để kích hoạt sự kiện thay đổi của React. Logic được thực hiện trong ChangeEventPlugin.js và có các nhánh mã khác nhau cho các loại đầu vào và trình duyệt khác nhau. Hơn nữa, các chi tiết triển khai khác nhau giữa các phiên bản của React.

Tôi đã xây dựng phản ứng-kích hoạt-thay đổi để thực hiện điều này, nhưng nó được sử dụng để thử nghiệm, không phải là phụ thuộc vào sản xuất:

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

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

CodePen


1

vì chúng tôi sử dụng các hàm để xử lý một sự kiện onchange, chúng tôi có thể làm như sau:

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

Tôi đã tìm thấy điều này trên các số báo Github của React: Hoạt động như một sự quyến rũ (v15.6.2)

Đây là cách tôi triển khai cho kiểu nhập Văn bản:

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)
    }
  }

Điều này không hoạt động nếu giá trị hộp văn bản giống với giá trị bạn đang chuyển cho setNativeValue
Arun

0

Đối với HTMLSelectElement, tức là<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

Nếu bạn đang sử dụng Backbone và React, tôi khuyên bạn nên sử dụng một trong những cách sau,

Cả hai đều giúp tích hợp các mô hình và bộ sưu tập Backbone với các khung nhìn React. Bạn có thể sử dụng các sự kiện Backbone giống như bạn làm với các khung nhìn Backbone. Tôi đã thử cả hai và không thấy có nhiều sự khác biệt ngoại trừ một là bản mixin và các thay đổi khác React.createClassthành React.createBackboneClass.


Hãy cẩn thận với những cầu nối "theo hướng sự kiện" giữa react và backbone Plugin đầu tiên sử dụng setProps mà không quan tâm đến mức mount của thành phần. Đó là một sai lầm. Thứ hai chủ yếu dựa vào forceUpdate và suy nghĩ rằng chỉ có thể chỉ định một mô hình cho một thành phần, đây không phải là tình huống phổ biến. Hơn nữa, nếu bạn chia sẻ mô hình trên giao diện người dùng phức tạp với các thành phần phản ứng và đã quên hủy đăng ký khỏi các sự kiện cập nhật vô ích gây ra forceUpdate, thì bạn có thể rơi vào kết xuất đệ quy, hãy lưu ý về điều đó.
wallice
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.