react js에서 onchange 이벤트를 트리거하는 가장 좋은 방법은 무엇입니까?


112

Backbone + ReactJS 번들을 사용하여 클라이언트 측 앱을 빌드합니다. 악명 높은 것에 크게 의존하여 valueLink양방향 바인딩을 위해 ReactJS 인터페이스를 지원하는 자체 래퍼를 통해 모델에 직접 값을 전파합니다.

이제 우리는 문제에 직면했습니다.

우리는이 jquery.mask.js프로그램 따라서 화재 이벤트를 반응하지 않는 입력 값의 형식을 플러그인. 이 모든 것이 모델이 사용자 입력에서 형식화되지 않은 값을 수신 하고 플러그인 에서 형식화 된 값을 놓치는 상황으로 이어집니다 .

React는 브라우저에 따라 많은 이벤트 처리 전략을 가지고있는 것 같습니다. React가 그것을 듣도록 특정 DOM 요소에 대한 변경 이벤트를 트리거하는 일반적인 방법이 있습니까?


답변:


174

React 16 및 React> = 15.6의 경우

.value=React 라이브러리가 입력 값 setter를 재정의하기 때문에 Setter 가 원하는대로 작동하지 않지만 inputas 컨텍스트 에서 직접 함수를 호출 할 수 있습니다 .

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

textarea 요소 prototype의 경우 HTMLTextAreaElement클래스 를 사용해야합니다 .

새로운 코드 펜 예제 .

이 기여자에 대한 모든 크레딧 및 그의 솔루션에

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

예제코드 펜을 만들었습니다.

새 플래그가 필요한 이유를 이해하기 위해이 주석이 매우 유용 하다는 것을 알았습니다 .

React의 입력 로직은 이제 값당 두 번 이상 발생하지 않도록 변경 이벤트의 중복을 제거합니다. 브라우저 onChange / onInput 이벤트와 DOM 노드 값 소품 (JavaScript를 통해 값을 업데이트 할 때)에 대한 설정을 모두 수신합니다. 이것은 입력 값을 수동으로 업데이트하면 input.value = 'foo'다음 {target : input}과 함께 ChangeEvent를 전달한다는 의미의 부작용이 있습니다. React는 세트와 이벤트를 모두 등록합니다. 값이 여전히` 'foo인지 확인합니다. ', 중복 이벤트로 간주하고 삼키십시오.

이것은 "실제"브라우저에서 시작된 이벤트가 element.value에 대한 집합을 트리거하지 않기 때문에 정상적인 경우에 잘 작동합니다. 시뮬레이션 된 플래그로 트리거 한 이벤트에 태그를 지정하여이 논리를 비밀리에 탈출 할 수 있으며 반응은 항상 이벤트를 발생시킵니다. https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128


1
감사합니다 이것은 react-dom ^ 15.6.0에서 작동했습니다. 그러나 React 16.0 이후에 이것이 작동을 멈춘 것 같습니다. 변경 이벤트를 트리거하기 위해 시뮬레이션 된 플래그를 사용하는 대안에 대한 아이디어가 있습니까?
Qwerty

여기에 힌트를 줄 지점이 있다고 믿으십시오. reactjs.org/blog/2017/09/26/react-v16.0.html#breaking-changes 그것이 무엇인지 아십니까?
Qwerty 2017

@Qwerty 나는 내 대답을 업데이트했습니다, 그것이 당신을 위해 작동 할지도 모릅니다
Grin

그리고 어떤 버튼 onClick? 이런 종류의 사건을 일으키기 위해 무엇을해야합니까?
Bender

@Bender 는 요소에서 네이티브 클릭 메서드를 호출합니다.
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은 편리하지 않습니다.
Michael

@Michael IE11에서 코드를 시도 중이며 reactjs 입력 필드에서 작동하지 않습니다. 일반 HTML 입력에서 작동합니다. 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

이 답변이 조금 늦게 오는 것을 알고 있지만 최근 비슷한 문제에 직면했습니다. 중첩 된 구성 요소에서 이벤트를 트리거하고 싶었습니다. 라디오 및 체크 박스 유형 위젯 (체크 박스 및 / 또는 라디오 버튼처럼 작동하는 div)이 포함 된 목록이 있었고 애플리케이션의 다른 위치에 누군가가 도구 상자를 닫으면 하나를 선택 취소해야했습니다.

나는 이것이 모범 사례인지 확실하지 않지만 매우 간단한 해결책을 찾았지만 작동합니다.

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

이것은 domNode에서 클릭 이벤트를 트리거하고 react를 통해 연결된 핸들러가 실제로 호출되었으므로 누군가가 요소를 클릭하면 예상하는 것처럼 작동합니다. 나는 onChange를 테스트하지 않았지만 작동해야하며 이것이 실제로 오래된 버전의 IE에서 어떻게 공정 할 지 확실하지 않지만 MouseEvent는 적어도 IE9 이상에서 지원된다고 생각합니다.

내 구성 요소가 매우 작기 때문에 (내 응용 프로그램의 일부만 반응을 사용했기 때문에 (아직 배우고 있기 때문에) dom 노드에 대한 참조를 얻지 않고도 다른 방식으로 동일한 것을 달성 할 수 있었기 때문에 특정 사용 사례에서 결국이에서 멀어졌습니다.

최신 정보:

다른 사람들이 주석에서 언급했듯이 this.refs.refnamedom 노드에 대한 참조를 얻는 데 사용하는 것이 좋습니다 . 이 경우 refname은를 통해 구성 요소에 연결 한 참조 <MyComponent ref='refname' />입니다.


1
ID 대신 React.findDOMNode기능을 사용할 수 있습니다 . goo.gl/RqccrA
m93a

2
> ID 대신 React.findDOMNode 함수를 사용할 수 있습니다. 아니면 추가 ref하여 요소에 다음 사용this.ref.refName.dispatchEvent
silkAdmin

프레임 워크는 그 이후로 변경되었을 가능성이 가장 높습니다. this.ref s .refname
nclord

1
문자열 참조는 현재 레거시로 간주되며 향후 더 이상 사용되지 않습니다. 콜백 참조는 DOM 노드를 추적하는 데 선호되는 방법입니다.
dzv3

9

ReactTestUtils를 사용하여 이벤트를 시뮬레이션 할 수 있습니다. 있지만 이는 단위 테스트 용으로 설계되었습니다.

이 경우 valueLink를 사용하지 않고 플러그인에 의해 발생한 변경 이벤트를 수신하고 이에 대한 응답으로 입력 상태를 업데이트하는 것이 좋습니다. 양방향 바인딩은 다른 무엇보다 데모로 더 유용합니다. 순수한 양방향 바인딩이 대부분의 응용 프로그램에 적합하지 않으며 일반적으로 앱의 상호 작용을 설명하기 위해 더 많은 응용 프로그램 논리가 필요하다는 사실을 강조하기 위해 애드온에 포함됩니다.


나는 대답이 그런 것일까 두려웠다 (. 문제는 React가 보내기 / 받기 워크 플로에 너무 엄격하다는 것이다. 특히 사용자 입력에 반응 할 수 있도록 onChange 핸들러를 지정해야하며, 제어되지 않은 상태를 제외하고는 다른 방법은 없다. 제 경우에는 규칙을 따르기 위해서만이 핸들러를 선언해야하지만 관련 입력은 jquery에 의해 트리거 된 onchange 이벤트에서
나옵니다 .React

1
그리고 ... ReactTestUtils를 사용하려는 경우 ...ReactTestUtils.Simulate.change(ReactDOM.findDOMNode(this.fieldRef))
colllin

8

Grin / Dan Abramov의 답변을 확장하면 여러 입력 유형에서 작동합니다. 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
일부 요소에는 작동하지 않습니다. 이벤트를 위해 '입력'대신 '변경'이 필요합니다.
styks

3

임의의 요소에 대한 변경 이벤트를 트리거하면 추론하기 어려운 구성 요소간에 종속성이 생성됩니다. React의 단방향 데이터 흐름을 고수하는 것이 좋습니다.

React의 변경 이벤트를 트리거하는 간단한 스 니펫은 없습니다. 로직은 ChangeEventPlugin.js에서 구현되며 다양한 입력 유형 및 브라우저에 대해 서로 다른 코드 분기가 있습니다. 또한 구현 세부 사항은 React 버전에 따라 다릅니다.

나는 일을하는 react-trigger-change 를 만들었지 만 프로덕션 종속성이 아닌 테스트에 사용하도록 의도되었습니다.

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

나는 React의 Github 문제에서 이것을 발견했습니다 : Works like a charm (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에 전달하는 값과 같으면 작동하지 않습니다
Arun

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 뷰와 통합하는 데 도움이됩니다. 백본보기에서와 마찬가지로 백본 이벤트를 사용할 수 있습니다. 나는 모두 다뤄했고 하나는 믹스 인 다른 변화이다 제외하고는 많은 차이 보지 못했어요 React.createClassReact.createBackboneClass.


반응과 백본 사이의 "이벤트 중심"브리지에주의하십시오. First 플러그인은 구성 요소 마운트 수준을 무시하고 setProps를 사용합니다. 실수입니다. 두 번째는 forceUpdate에 크게 의존하며 구성 요소에 대해 하나의 모델 만 할당 할 수 있다는 점을 염두에두고 있습니다. 이는 일반적인 상황이 아닙니다. 더욱이, 반응 구성 요소가있는 복잡한 UI에서 모델을 공유하고 forceUpdate를 유발하는 쓸모없는 업데이트 이벤트를 구독 취소하는 것을 잊은 경우 재귀 렌더링에 빠질 수 있습니다.
wallice
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.