it-swarm.com.ru

Каков наилучший способ вызвать событие onchange в реакции JS

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

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

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

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

76
wallice

Для Реакции 16 и Реакта> = 15,6

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

Я сделал codepen с примером

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

Логика ввода в React теперь дедуплирует события изменения, чтобы они не срабатывали более одного раза за значение. Он слушает оба браузера onChange/onInput события, а также устанавливает значение проп в DOM-узле (когда вы обновляете значение через javascript). Это побочный эффект означает, что если вы обновите значение ввода вручную input.value = 'foo' и отправьте ChangeEvent с {target: input} React зарегистрирует оба набора и событие, видите, что его значение по-прежнему `` foo ', считайте его дубликатом событие и проглотить его.

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

80
Grin

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

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

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

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

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

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

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

Обновление:

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

18
Robert-W

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

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

7
Sophie Alpert

Расширяя ответ Грина/Дана Абрамова, это работает для нескольких типов ввода. Проверено в реакции> = 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);

    }

};
2
cjpete

хорошо, так как мы используем функции для обработки события 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
four-eyes-04-04

Я обнаружил это в проблемах React с Github: работает как талисман (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)
    }
  }
1
a2ron44

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

Нет простого фрагмента для запуска события изменения React. Логика реализована в ChangeEventPlugin.js и есть разные ветви кода для разных типов ввода и браузеров. Более того, детали реализации варьируются в зависимости от версии React.

Я построил response-trigger-change , который делает это, но он предназначен для тестирования, а не в качестве производственной зависимости:

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

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

CodePen

1
Vitaly Kuznetsov

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

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

0
Blaine Hatab