React: Bug: atualização de estado não relacionado em onKeyDown bloqueia onChange e quebra o componente controlado

Criado em 9 jul. 2020  ·  3Comentários  ·  Fonte: facebook/react

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

Passos para reproduzir

  1. entrar no espaço
  2. elemento de entrada não mostra espaço na tela

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

O comportamento atual

elemento de entrada não pode receber o espaço

O comportamento esperado

elemento de entrada pode receber o espaço

DOM Unconfirmed Bug

Todos 3 comentários

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.

Esta página foi útil?
0 / 5 - 0 avaliações