React: Error: actualización de estado no relacionada en onKeyDown bloquea onChange y rompe el componente controlado

Creado en 9 jul. 2020  ·  3Comentarios  ·  Fuente: facebook/react

Hola, uso el modo de entrada controlada y me suscribo al evento onKeyDown y onChange . Activará un efecto cuando el usuario ingrese al espacio. Sin embargo, el espacio no se muestra en la pantalla. ¿Es esto un error en React o hay algún problema con la forma en que lo estoy usando? Pero suscribirse a onKeyUp es normal. Esto es confuso.

Reaccionar versión: 16.13.1 y anterior

Pasos para reproducir

  1. entrar en el espacio
  2. el elemento de entrada no muestra espacio en la pantalla

Enlace al ejemplo 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

El comportamiento actual

el elemento de entrada no puede recibir el espacio

El comportamiento esperado

El elemento de entrada puede recibir el espacio.

DOM Unconfirmed Bug

Todos 3 comentarios

Parece que la actualización de estado en el controlador onKeyDown interrumpe el evento y evita que se llame al controlador onChange . Debido a que los componentes controlados muestran solo lo que usted les dice (el estado value en su ejemplo), esto hace que el carácter de espacio nunca aparezca.

Esto me parece un error en la forma en que estos dos eventos interactúan.

cc @trueadm @gaearon por su experiencia en sistemas de eventos

Hola, soy nuevo aquí, tratando de aprender arquitectura React.

@bvaughn Aquí hay algo que encontré sobre el problema.

onKeyDown evento onChange sino que se activa antes de que se modifique la entrada. Básicamente, onKeyDown va antes que onChange porque la entrada de texto ocurre en onKeyUp .

En este caso particular, el problema es que dentro de keyDown , cuando setCode llama, en el primer dispatchAction _Fiber_ tiene memoizedState sin el nuevo carácter (obviamente) y e que se agregará. Al final, agrega el nuevo carácter a la entrada. Entonces useEffect() activa dispatchAction nuevamente dentro de la misma cola_ (no estoy seguro si es cola) pero no sabe nada acerca de los accesorios actualizados. Como resultado, cuando commitRoot , restaura pendingProps que son los mismos que estaban en memoizedState . En resumen, se agrega el carácter (espacio en este ejemplo) e inmediatamente se restaura el estado anterior sin el nuevo carácter (espacio), por lo que parece que no agrega nada.

Como ejemplo de no usar _misma cola_ (?), Este ejemplo en particular:

useEffect(() => {
    if (code === 'add') {
      setCount(c => c + 1)
    } else {
      setCount(c => c - 1)
    }
  }, [code, setCount])

podría modificarse a:

useEffect(() => {
    if (code === 'add') {
      setTimeout(() => setCount(c => c + 1), 0)
    } else {
      setTimeout(() => setCount(c => c - 1), 0)
    }
  }, [code, setCount])

y funcionará.

Espero que tenga sentido.

Me encontré con este mismo problema, envolviendo mi llamada setState en un setTimeout en el controlador keydown lo solucionó.

Tengo curiosidad por saber si esto es algo que debería funcionar de manera diferente bajo el capó en React o si este error apunta a un error mayor o un patrón incorrecto en el lado del desarrollador.

¿Fue útil esta página
0 / 5 - 0 calificaciones