Вы хотите запросить функцию или сообщить об ошибке ?
Ошибка!
Каково текущее поведение?
При рендеринге элемента input
типа checkbox
с обработчиком onClick
и onChange
, onChange
по-прежнему вызывается, даже если event.preventDefault()
вызывается в обработчике onClick
.
Если текущее поведение является ошибкой, предоставьте шаги для воспроизведения и, если возможно, минимальную демонстрацию проблемы через https://jsfiddle.net или аналогичный (шаблон: https://jsfiddle.net/reactjs/69z2wepo/).
Какое ожидаемое поведение?
Вызов event.preventDefault
в обработчике onClick
должен предотвратить выполнение действия по умолчанию (или отменить его действие), которое заключается в обновлении значения элемента input
. Это должно остановить вызов любого прослушивателя событий change
. См. Https://jsfiddle.net/L1eskzsq/ для ожидаемого поведения
Какие версии React и какой браузер / ОС подвержены этой проблеме?
Протестировано с использованием сборки из master, macOS 10.12.2, проверено в:
Safari 10.0.2 вызывает прослушиватель событий change
в обоих случаях.
Событие change
создается ChangeEventPlugin
в качестве ответа на topChange
, прежде чем ваш обработчик onClick
даже будет вызван (так до preventDefault
call), здесь: https://github.com/facebook/react/blob/master/src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js#L338
Этот плагин пытается выяснить, изменится ли значение флажка, и если да, он поставит в очередь событие change
. Он использует функциональность inputValueTracking
для сравнения предыдущего значения со следующим значением, чтобы принять это решение, здесь: https://github.com/facebook/react/blob/master/src/renderers/dom/shared/ inputValueTracking.js # L154
nextValue
берется непосредственно из DOM следующим образом: value = isCheckable(node) ? '' + node.checked : node.value;
https://github.com/facebook/react/blob/master/src/renderers/dom/shared/inputValueTracking.js# L56
Проблема в том, что в этот момент значение node.checked
временно истинно, поэтому после первого щелчка оно будет:
lastValue == false
(правильно)
nextValue == true
(неверно)
и при следующем щелчке по флажку событие change
больше не запускается, потому что значения:
lastValue == true
(неверно)
nextValue == true
(неверно)
В целом проблема, похоже, заключается в том, что React спрашивает DOM, каково значение checked
узла, прежде чем можно будет вызвать preventDefault в onClick
.
Спасибо @karelskopek и @Mintaffee за вашу помощь в этом.
@aweary Как вы думаете, стоит ли это решать?
Нет прогресса с этим? Я использую React v16.2, и поведение такое же - onChange запускается в первый раз, а последующие щелчки не запускают его. Я бы сказал, здесь достаточно приоритетный вопрос ..
Это связано? preventDefault
вызывается в поле флажка onChange
вызывает рассинхронизацию React и состояния браузера: https://codesandbox.io/s/0qpr936xkv
По-прежнему проблема в React 16.8.6. Один из возможных обходных путей - сделать все в обработчике onClick
:
const radios = ['one', 'two', 'three'];
class Row extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.onChange = this.onChange.bind(this);
}
render() {
const { value } = this.props;
return (
<div className="row">
<div className="radio-group">
{radios.map(radio => (
<label key={radio} className="radio">
<input
type="radio"
name="radio"
value={radio}
checked={radio === value}
onClick={this.onClick}
onChange={this.onChange}
/><span>Radio</span>
</label>
))}
</div>
</div>
);
}
onClick(event) {
// Check something to prevent radio change
if (...) {
event.preventDefault();
return;
}
// Sync value in the store
this.props.setValue(event.target.value);
}
onChange() {
// We need this empty handler to suppress React warning:
// "You provided a `checked` prop to a form field without an `onChange` handler.
// This will render a read-only field. If the field should be mutable use `defaultChecked`.
// Otherwise, set either `onChange` or `readOnly`"
}
}
Если вы используете управляемый компонент, я нашел еще один вариант - просто оставить значение в неизменном состоянии:
onChange(event) {
// Check something to prevent radio change
if (...) {
return;
}
// Sync value in the store
this.props.setDateTime(event.target.value);
}
Для более простого решения вы можете выполнить e.preventDefault для события onMouseDown, чтобы предотвратить запуск onChange.
onMouseDown(e) {
if (...) {
e.preventDefault();
return;
}
}
Самый полезный комментарий
Это связано?
preventDefault
вызывается в поле флажкаonChange
вызывает рассинхронизацию React и состояния браузера: https://codesandbox.io/s/0qpr936xkv