Bonjour, j'utilise le mode contrôlé par entrée et je m'abonne à l'événement onKeyDown
et onChange
. Cela déclenchera un effet lorsque l'utilisateur entrera dans l'espace. Cependant, l'espace ne s'affiche pas à l'écran. Est-ce un bug sur React ou y a-t-il quelque chose qui ne va pas avec la façon dont je l'utilise? Mais s'abonner à onKeyUp
est normal. C'est confus.
Version React: 16.13.1 et ancienne
Lien vers l'exemple de code:
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
l'élément d'entrée ne peut pas recevoir l'espace
l'élément d'entrée peut recevoir l'espace
Il semble que la mise à jour de l'état dans le gestionnaire onKeyDown
interrompt l'événement et empêche le gestionnaire onChange
d'être appelé. Étant donné que les composants contrôlés affichent uniquement ce que vous leur dites (l'état value
dans votre exemple), le caractère espace n'apparaît jamais.
Cela me semble être un bug dans la façon dont ces deux événements interagissent.
cc @trueadm @gaearon pour leur expertise en systèmes d'événements
Salut, je suis nouveau ici, essayant d'apprendre l'architecture React.
@bvaughn Voici quelque chose que j'ai trouvé sur le problème.
onKeyDown
événement onChange
plutôt qu'il se déclenche avant que l'entrée ne soit modifiée. Fondamentalement, onKeyDown
précède onChange
car la saisie de texte se produit dans onKeyUp
.
Dans ce cas particulier, le problème est que dans keyDown
, quand setCode
appelle, sur le premier dispatchAction
_Fiber_ a memoizedState
sans le nouveau caractère (évidemment) et e
qui va être ajouté. En fin de compte, il ajoute le nouveau caractère à l'entrée. Ensuite, useEffect()
déclenche à nouveau dispatchAction
dans la même file d'attente (je ne sais pas si c'est la file d'attente) mais il ne sait rien des accessoires mis à jour. En conséquence, quand il commitRoot
, il restaure pendingProps
qui sont les mêmes que dans memoizedState
. En bref, le caractère (espace dans cet exemple) est ajouté, puis immédiatement l'ancien état est restauré sans le nouveau caractère (espace), il semble donc qu'il n'ajoute rien.
À titre d'exemple de ne pas utiliser la même file d'attente (?), Cet exemple particulier:
useEffect(() => {
if (code === 'add') {
setCount(c => c + 1)
} else {
setCount(c => c - 1)
}
}, [code, setCount])
pourrait être modifié en:
useEffect(() => {
if (code === 'add') {
setTimeout(() => setCount(c => c + 1), 0)
} else {
setTimeout(() => setCount(c => c - 1), 0)
}
}, [code, setCount])
et cela fonctionnera.
J'espère que ça a du sens.
Ran dans ce même problème, enveloppant mon appel setState dans un setTimeout dans le gestionnaire de keydown l'a corrigé.
Je suis curieux de savoir si c'est quelque chose qui devrait fonctionner différemment sous le capot dans React ou si cette erreur indique une erreur plus grave ou un mauvais modèle du côté des développeurs?