Olá, eu uso o modo de entrada controlada e me inscrevo nos eventos onKeyDown
e onChange
. Isso irá desencadear um efeito quando o usuário entrar no espaço. No entanto, o espaço não é exibido na tela. É um bug no React ou há algo errado com a maneira como o estou usando. Mas assinar onKeyUp
é normal. Isso é confuso.
Versão do React: 16.13.1 e anterior
Link para o exemplo de código:
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
elemento de entrada não pode receber o espaço
elemento de entrada pode receber o espaço
Parece que a atualização de estado no manipulador onKeyDown
interrompe o evento e evita que o manipulador onChange
seja chamado. Como os componentes controlados mostram apenas o que você diz a eles (o estado value
em seu exemplo), isso faz com que o caractere de espaço nunca apareça.
Isso parece um bug para mim na forma como esses dois eventos interagem.
cc @trueadm @gaearon por sua experiência em sistemas de eventos
Olá, sou novo aqui, tentando aprender a arquitetura React.
@bvaughn Aqui está algo que descobri sobre o problema.
onKeyDown
event não bloqueia onChange
vez de disparar antes de a entrada ser modificada. Basicamente, onKeyDown
vai antes de onChange
porque a entrada de texto acontece em onKeyUp
.
Neste caso particular, o problema é que dentro de keyDown
, quando setCode
chama, no primeiro dispatchAction
_Fiber_ tem memoizedState
sem o novo caractere (obviamente) e e
que será adicionado. No final, ele adiciona o novo caractere à entrada. Então useEffect()
dispara dispatchAction
novamente dentro da _mesma fila_ (não tenho certeza se é fila) mas não sabe nada sobre adereços atualizados. Como resultado, quando commitRoot
, ele restaura pendingProps
que são os mesmos que estavam em memoizedState
. Resumindo, o caractere (espaço neste exemplo) é adicionado e, em seguida, o estado antigo é restaurado imediatamente sem o novo caractere (espaço), então parece que não adiciona nada.
Como um exemplo de não usar _same queue_ (?), Este exemplo particular:
useEffect(() => {
if (code === 'add') {
setCount(c => c + 1)
} else {
setCount(c => c - 1)
}
}, [code, setCount])
pode ser modificado para:
useEffect(() => {
if (code === 'add') {
setTimeout(() => setCount(c => c + 1), 0)
} else {
setTimeout(() => setCount(c => c - 1), 0)
}
}, [code, setCount])
e vai funcionar.
Eu espero que faça sentido.
Encontrei esse mesmo problema, envolvendo minha chamada setState em um setTimeout no manipulador keydown corrigiu-o.
Estou curioso para saber se isso é algo que deveria funcionar de forma diferente nos bastidores no React ou se esse erro aponta para um erro maior ou padrão ruim do lado do desenvolvedor.