Что является лучшим способом, чтобы вызвать событие onChange в реагировать JS


мы используем Backbone + ReactJS bundle для создания клиентского приложения. Сильно полагаясь на пресловутый valueLink мы распространяем значения непосредственно на модель через собственную оболочку, которая поддерживает интерфейс ReactJS для двухсторонней привязки.

теперь мы столкнулись с проблемой:

у нас есть jquery.mask.js плагин, который форматирует входное значение программно, таким образом, он не запускает события реакции. Все это приводит к ситуации, когда модель получает неформатированный значения из пользовательского ввода и пропускает форматированные из плагина.

похоже, что React имеет множество стратегий обработки событий в зависимости от браузера. Есть ли какой-либо общий способ вызвать событие изменения для конкретного элемента DOM, чтобы React услышал его?

8 56

8 ответов:

для React 16 и React >=15.6

сеттер .value= не работает так, как мы хотели, потому что библиотека React переопределяет задатчик входных значений, но мы можем вызвать функцию непосредственно на 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);

для элемента textarea вы должны использовать prototype на HTMLTextAreaElement класса.

новая пример codepen.

все кредиты для этот вклад и его решение

устаревший ответ только для React

С react-dom ^15.6.0 можно использовать simulated флаг на объекте события для события, чтобы пройти через

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

Я codepen с примером

чтобы понять, зачем нужен новый флаг я нашел комментарий очень полезная:

входной логики в теперь реагировал на происходящее изменение уровней, поэтому они не стреляют больше чем один раз в стоимость. Он слушает как браузер onChange/onInput события, а также наборы на опоре значения узла DOM (при обновлении значение через JavaScript). Это имеет побочный эффект означает, что если вы обновите значение ввода вручную.значение = ' foo ' затем отправить a ChangeEvent с { цель: ввод } реагировать будет зарегистрировать и установить и событие, см. Его значение по-прежнему "foo", считайте его дубликатом событие и проглотить его.

это прекрасно работает в нормальных случаи, потому что" реальный " браузер инициирован событие не вызывает наборы на элементе.значение. Вы можете выбраться из эта логика тайно помечает событие, которое вы запускаете с имитацией флаг и реагировать всегда будет стрелять событие. https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128

по крайней мере на текстовых входов, получается, что onChange прослушивает входные события:

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

Я знаю, что этот ответ немного поздно, но я недавно столкнулась с подобной проблемой. Я хотел вызвать событие на вложенном компоненте. У меня был список с виджетами типа radio и check box (они были divs, которые вели себя как флажки и/или переключатели), а в другом месте приложения, если кто-то закрыл панель инструментов, мне нужно было снять ее.

Я нашел довольно простое решение, не уверен, что это лучшая практика, но она работает.

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

этот вызвал событие click на domNode, и мой обработчик, прикрепленный через react, действительно был вызван, поэтому он ведет себя так, как я ожидал бы, если бы кто-то нажал на элемент. Я не тестировал onChange, но он должен работать, и не уверен, как это будет справедливо в действительно старых версиях IE, но я считаю, что MouseEvent поддерживается по крайней мере в IE9 и выше.

Я в конечном итоге отошел от этого для моего конкретного случая использования, потому что мой компонент был очень маленьким (только часть моего приложения использовала react поскольку я все еще изучаю его), и я мог бы достичь того же самого другим способом, не получая ссылок на узлы dom.

обновление:

как уже сказали в комментариях, лучше использовать this.refs.refname чтобы получить ссылку на узел dom. В этом случае refname-это ссылка, которую вы прикрепили к своему компоненту через <MyComponent ref='refname' />.

вы можете имитировать события с помощью ReactTestUtils но это предназначено для модульного тестирования.

Я бы рекомендовал не использовать valueLink для этого случая и просто слушать события изменения, запущенные плагином, и обновлять состояние ввода в ответ. Двусторонняя привязка utils больше как демонстрация, чем что-либо еще; они включены в аддоны только для того, чтобы подчеркнуть тот факт, что чистая двусторонняя привязка не подходит для большинства приложений и что вам обычно нужно больше логика приложения для описания взаимодействий в вашем приложении.

запуск событий изменения на произвольных элементах создает зависимости между компонентами, о которых трудно рассуждать. Лучше придерживаться одностороннего потока данных 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

ну так как мы используем функции для обработки события 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)} />
  );
 }
}

расширяя ответ от 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);

    }

};

Если вы используете позвоночник и реагируете, я бы рекомендовал одно из следующих,

Они оба помогают интегрировать базовые модели и коллекции с React views. Вы можете использовать события магистрали так же, как Вы делаете с представлениями магистрали. Я баловался обоими и не видел большой разницы, кроме одного-это mixin, а другие изменения React.createClass до React.createBackboneClass.