React: Bug: mise à jour d'état non liée dans onKeyDown bloque onChange et interrompt le composant contrôlé

Créé le 9 juil. 2020  ·  3Commentaires  ·  Source: facebook/react

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

Étapes à suivre pour reproduire

  1. entrer dans l'espace
  2. l'élément d'entrée n'affiche pas d'espace sur l'écran

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

Le comportement actuel

l'élément d'entrée ne peut pas recevoir l'espace

Le comportement attendu

l'élément d'entrée peut recevoir l'espace

DOM Unconfirmed Bug

Tous les 3 commentaires

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?

Cette page vous a été utile?
0 / 5 - 0 notes