React: Bug: Pembaruan status tidak terkait di onKeyDown memblokir onChange dan merusak komponen yang dikontrol

Dibuat pada 9 Jul 2020  ·  3Komentar  ·  Sumber: facebook/react

Hai, Saya menggunakan mode Kontrol masukan dan berlangganan acara onKeyDown dan onChange . Ini akan memicu efek saat pengguna memasuki ruang angkasa. Namun, ruang tersebut tidak ditampilkan di layar. Apakah ini bug di React atau ada yang salah dengan cara saya menggunakannya. Tapi berlangganan onKeyUp itu normal. Ini membingungkan.

Versi React: 16.13.1 dan lama

Langkah-langkah untuk Mereproduksi

  1. memasuki ruang angkasa
  2. elemen masukan tidak menunjukkan ruang di layar

Tautan ke contoh kode:

import React, { useState,useEffect } from 'react'

const App = () => {
  const [count, setCount] = useState(0)
  const [value, setValue] = useState('')
  const [code, setCode] = useState('add')

  useEffect(() => {
    if (code === 'add') {
      setCount(c => c + 1)
    } else {
      setCount(c => c - 1)
    }
  }, [code, setCount])

  const keyDown = e => {
    if (e.keyCode === 32) {
      console.log('space~')
      if (code === 'add') {
        setCode('minus')
      } else {
        setCode('add')
      }
    }
  }

  const handleChange = e => {
    setValue(e.target.value)
  }

  return (
    <div>
      <div>{count}</div>
      <br />
      <input onKeyDown={keyDown} value={value} onChange={handleChange} />
    </div>
  )
}

export default App

Perilaku saat ini

elemen masukan tidak dapat menerima ruang

Perilaku yang diharapkan

elemen masukan dapat menerima ruang

DOM Unconfirmed Bug

Semua 3 komentar

Sepertinya pembaruan status dalam penangan onKeyDown menyela kejadian dan mencegah penangan onChange dipanggil. Karena komponen terkontrol hanya menampilkan apa yang Anda perintahkan (status value dalam contoh Anda) ini mengakibatkan karakter spasi tidak pernah muncul.

Ini tampak seperti bug bagi saya dalam cara kedua peristiwa ini saling mempengaruhi.

cc @trueadm @gaearon atas keahlian sistem acara mereka

Hai, saya baru di sini, mencoba mempelajari arsitektur React.

@bvaughn Berikut adalah sesuatu yang saya temukan tentang masalah tersebut.

onKeyDown acara tidak memblokir onChange melainkan dipicu sebelum input diubah. Pada dasarnya, onKeyDown lebih awal dari onChange karena input teks terjadi di onKeyUp .

Dalam kasus khusus ini, masalahnya adalah bahwa dalam keyDown , ketika setCode memanggil, pada dispatchAction _Fiber_ memiliki memoizedState tanpa karakter baru (jelas) dan e yang akan ditambahkan. Pada akhirnya, ini menambahkan karakter baru ke input. Kemudian useEffect() memicu dispatchAction lagi dalam _same queue_ (tidak yakin apakah itu antrian) tetapi tidak tahu apa-apa tentang alat peraga yang diperbarui. Akibatnya, ketika commitRoot , itu mengembalikan pendingProps yang sama seperti di memoizedState . Singkatnya, karakter (spasi dalam contoh ini) ditambahkan dan kemudian status lama segera pulih tanpa karakter baru (spasi), jadi sepertinya tidak menambahkan apa pun.

Sebagai contoh tidak menggunakan _same queue_ (?), Contoh khusus ini:

useEffect(() => {
    if (code === 'add') {
      setCount(c => c + 1)
    } else {
      setCount(c => c - 1)
    }
  }, [code, setCount])

dapat dimodifikasi menjadi:

useEffect(() => {
    if (code === 'add') {
      setTimeout(() => setCount(c => c + 1), 0)
    } else {
      setTimeout(() => setCount(c => c - 1), 0)
    }
  }, [code, setCount])

dan itu akan berhasil.

Saya harap itu masuk akal.

Mengalami masalah yang sama ini, membungkus panggilan setState saya dalam setTimeout di penangan keydown memperbaikinya.

Saya penasaran untuk mengetahui apakah ini adalah sesuatu yang seharusnya bekerja secara berbeda di bawah tenda di React atau jika kesalahan ini menunjukkan kesalahan yang lebih besar atau pola buruk di sisi pengembang?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat