React: event.preventDefault di click handler tidak mencegah onChange dipanggil

Dibuat pada 17 Feb 2017  ·  6Komentar  ·  Sumber: facebook/react

Apakah Anda ingin meminta fitur atau melaporkan bug ?

Bug!

Bagaimana perilaku saat ini?

Saat merender elemen input tipe checkbox dengan handler onClick dan onChange , onChange masih dipanggil meskipun event.preventDefault() dipanggil dalam penangan onClick .

Jika perilaku saat ini adalah bug, berikan langkah-langkah untuk mereproduksi dan jika mungkin demo minimal dari masalah tersebut melalui https://jsfiddle.net atau yang serupa (template: https://jsfiddle.net/reactjs/69z2wepo/).

http://jsfiddle.net/rf3w7apc/

Apa perilaku yang diharapkan?

Memanggil event.preventDefault dalam penangan onClick seharusnya mencegah tindakan default terjadi (atau membatalkan efeknya), yaitu memperbarui nilai elemen input . Ini akan menghentikan pemroses acara change dari pemanggilan. Lihat https://jsfiddle.net/L1eskzsq/ untuk perilaku yang diharapkan

Versi React mana, dan browser / OS mana yang terpengaruh oleh masalah ini?

Diuji menggunakan build dari master, macOS 10.12.2, diverifikasi di:

  • Chrome 56.0.2924.87 (64-bit)
  • Firefox 51.0.1 (64-bit)

Safari 10.0.2 memanggil pendengar acara change dalam kedua kasus tersebut.

DOM Bug

Komentar yang paling membantu

Apakah ini terkait? preventDefault dipanggil di kotak centang onChange menyebabkan React dan status browser tidak sinkron: https://codesandbox.io/s/0qpr936xkv

Semua 6 komentar

Acara change sedang dibuat oleh ChangeEventPlugin sebagai tanggapan atas topChange , bahkan sebelum penangan onClick dipanggil (jadi sebelum preventDefault call), di sini: https://github.com/facebook/react/blob/master/src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js#L338

Plugin ini mencoba mencari tahu apakah nilai kotak centang akan berubah, dan jika ya itu akan mengantri acara change . Ini menggunakan fungsionalitas inputValueTracking untuk membandingkan nilai sebelumnya dengan nilai berikutnya untuk membuat keputusan ini, di sini: https://github.com/facebook/react/blob/master/src/renderers/dom/shared/ inputValueTracking.js # L154

nextValue diambil langsung dari DOM seperti ini: value = isCheckable(node) ? '' + node.checked : node.value; https://github.com/facebook/react/blob/master/src/renderers/dom/shared/inputValueTracking.js# L56

Masalahnya adalah bahwa nilai node.checked sementara benar pada saat ini, jadi setelah klik pertama adalah:

lastValue == false (benar)
nextValue == true (salah)

dan pada klik berikutnya pada kotak centang, peristiwa change tidak lagi diaktifkan, karena nilainya adalah:

lastValue == true (salah)
nextValue == true (salah)

Secara keseluruhan masalahnya tampaknya React menanyakan DOM berapa nilai checked dari node itu, sebelum preventDefault di onClick dapat dipanggil.

Terima kasih @karelskopek dan @Mintaffee atas bantuan Anda untuk menggali lebih dalam.

@aweary Menurut Anda apakah ini layak untuk

Tidak ada kemajuan dengan yang ini? Saya menggunakan React v16.2 dan perilakunya sama - onChange dipicu pertama kali dan klik berikutnya tidak memicunya. Saya akan mengatakan masalah prioritas cukup tinggi di sini ..

Apakah ini terkait? preventDefault dipanggil di kotak centang onChange menyebabkan React dan status browser tidak sinkron: https://codesandbox.io/s/0qpr936xkv

Masih masalah di React 16.8.6. Salah satu solusi yang mungkin adalah melakukan semuanya di onClick handler:

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`"
  }
}

Jika Anda menggunakan komponen terkontrol, opsi lain yang saya temukan adalah membiarkan nilai dalam status tidak berubah:

onChange(event) {
  // Check something to prevent radio change
  if (...) {
    return;
  }
  // Sync value in the store
  this.props.setDateTime(event.target.value);
}

Untuk solusi yang lebih sederhana, Anda dapat melakukan e.preventDefault pada peristiwa onMouseDown untuk mencegah pengaktifan onChange.

onMouseDown(e) { if (...) { e.preventDefault(); return; } }

Apakah halaman ini membantu?
0 / 5 - 0 peringkat