Estoy usando la forma de gancho de reacción con un reloj para detectar un cambio en <select> pero después de completar la selección, el reloj inicialmente tiene un valor incorrecto.
Aquí hay un código para ilustrar el problema:
import React, { useState, useEffect } from 'react'; import { useForm } from "react-hook-form"; function App() { const { register, watch } = useForm(); const watchFoo = watch("foo"); const [foos, setFoos] = useState([]); useEffect(() => { setFoos([{ id: 1, name: "one" }, { id: 2, name: "two" }]); }, []); return ( <form> <select {...register('foo')}> {foos.map((foo, i) => (<option key={`option_${i}`} value={foo.id}>{foo.name}</option>))} </select> <p>foo is {watchFoo}</p> </form> ); } export default App;https://codesandbox.io/s/mystifying-kate-8n9xu
El useEffect simula obtener los valores del servidor cuando el componente se carga por primera vez.
En el primer render, watchFoo tiene el valor indefinido como se esperaba. Después de llamar a setFoos , vuelve a mostrarse con el menú desplegable con el valor "uno" seleccionado, pero watchFoo tiene el valor "". Después de cambiar el valor en el menú desplegable, watchFoo es correcto, pero solo el valor inicial es incorrecto.
¿Hay alguna solución o solución para esto?
El valor "uno" en realidad no ha tenido un evento de selección: es solo la primera <option> en la lista, por lo que el <select> representado lo muestra. El elemento de selección en sí mismo nunca disparó los eventos para actualizar el estado del formulario interno con el nuevo estado del cuadro de selección. Esto provoca un desacoplamiento del estado del formulario interno y el estado del formulario como se representa en la página, porque el navegador mostrará la primera opción en caso de que ninguna de las opciones tenga la propiedad selected ( RFC 1866 ).
Puede agregar un useEffect adicional para actualizar el estado de la forma de enlace cuando cambien los foos:
const { register, watch, setValue } = useForm(); useEffect(() => { if(watchFoo === "" && foos.length > 0) { setValue("foo", foos[0].id); } }, [watchFoo, setValue, foos])Otra opción más simple sería simplemente agregar una opción en blanco invisible como su primera entrada:
<option style={{display: "none"}}></option>Esto daría como resultado que su selección muestre un valor en blanco de forma predeterminada, pero sin dejar la opción en blanco como una opción de selección en su menú desplegable. Esto da como resultado que el formulario refleje correctamente el estado de su estado de formulario interno, pero no preseleccionaría la primera opción del servidor. Seleccione la técnica que mejor se adapte a su caso de uso.