Привет, я использую режим управления вводом и подписываюсь на события onKeyDown
и onChange
. Это вызовет эффект, когда пользователь войдет в пространство. Однако пространство не отображается на экране. Это ошибка в React или что-то не так с тем, как я его использую? Но подписаться на onKeyUp
- это нормально. Это сбивает с толку.
Версия React: 16.13.1 и старше
Ссылка на пример кода:
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
элемент ввода не может получить пробел
элемент ввода может получать пробел
Похоже, что обновление состояния в обработчике onKeyDown
прерывает событие и предотвращает вызов обработчика onChange
. Поскольку контролируемые компоненты показывают только то, что вы им говорите (состояние value
в вашем примере), это приводит к тому, что пробел никогда не появляется.
Мне это кажется ошибкой во взаимодействии этих двух событий.
cc @trueadm @gaearon за их опыт в системе событий
Привет, я здесь новенький, пытаюсь изучить архитектуру React.
@bvaughn Вот кое-что, что я нашел о проблеме.
Событие onKeyDown
не блокирует onChange
а срабатывает раньше, чем изменяется ввод. По сути, onKeyDown
идет раньше, чем onChange
потому что ввод текста происходит в onKeyUp
.
В этом конкретном случае проблема заключается в том, что внутри keyDown
, когда setCode
вызывает, на первом dispatchAction
_Fiber_ есть memoizedState
без нового символа (очевидно) и e
который будет добавлен. В конце концов, он добавляет на вход новый символ. Затем useEffect()
снова запускает dispatchAction
в _ той же очереди_ (не уверен, что это очередь), но он ничего не знает об обновленных свойствах. В результате, когда он commitRoot
, он восстанавливает pendingProps
которые такие же, как и в memoizedState
. Короче говоря, символ (пробел в этом примере) добавляется, а затем сразу восстанавливается старое состояние без нового символа (пробела), поэтому похоже, что он ничего не добавляет.
В качестве примера того, что _same queue_ (?) Не используется, этот конкретный пример:
useEffect(() => {
if (code === 'add') {
setCount(c => c + 1)
} else {
setCount(c => c - 1)
}
}, [code, setCount])
можно изменить на:
useEffect(() => {
if (code === 'add') {
setTimeout(() => setCount(c => c + 1), 0)
} else {
setTimeout(() => setCount(c => c - 1), 0)
}
}, [code, setCount])
и это будет работать.
Надеюсь, это имеет смысл.
Возникла та же проблема, заключив мой вызов setState в setTimeout в обработчике keydown, исправив его.
Мне любопытно узнать, должно ли это работать по-другому под капотом в React, или эта ошибка указывает на более серьезную ошибку или плохой шаблон со стороны разработчика?