react-hook-form watch在呈现select元素后值错误



我正在使用react-hook-form与手表来检测<select>中的a变化,但在填充选择后,手表最初具有错误的值。

下面是一些代码来说明这个问题:

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

useEffect模拟在组件首次加载时从服务器获取值。

第一次渲染时,watchFoo的值如预期的那样未定义。调用setFoos后,它再次呈现,下拉框的值为"one";选择,但watchFoo的值为"。更改下拉框中的值后,watchFoo是正确的,但错误的只是初始值。

对此有任何解决方案或变通方法吗?

value实际上没有发生选择事件:它只是列表中的第一个<option>,因此呈现的<select>显示了它。select元素本身从未触发事件以使用新的选择框状态更新内部表单状态。这将导致内部表单状态和页面中表示的表单状态的解耦,因为如果没有一个选项具有selected属性(RFC 1866),浏览器将显示第一个选项。

当foos发生变化时,你可以添加一个额外的useEffect来更新hookform状态:

const { register, watch, setValue } = useForm();
useEffect(() => {
if(watchFoo === "" && foos.length > 0) {
setValue("foo", foos[0].id);
}
}, [watchFoo, setValue, foos])

另一个更简单的选项是在第一个条目中添加一个不可见的空白选项:

<option style={{display: "none"}}></option>

这将导致您的选择在默认情况下显示一个空白值,但不会在您的下拉菜单中留下空白选项作为选择选项。这将导致表单正确地反映内部表单状态的状态,但不会从服务器预选第一个选项。选择最适合您用例的技术。