如何使用 useEffect() 更改 React-Hook-Form defaultValue?

Posted

技术标签:

【中文标题】如何使用 useEffect() 更改 React-Hook-Form defaultValue?【英文标题】:How to change React-Hook-Form defaultValue with useEffect()? 【发布时间】:2020-09-26 06:32:39 【问题描述】:

我正在创建一个页面供用户使用 React-Hook-Form 更新个人数据。 加载页面后,我使用useEffect 获取用户当前的个人数据并将其设置为表单的默认值。

我将获取的值放入<Controller />defaultValue 中。 但是,它只是没有显示在文本框中。 这是我的代码:

import React, useState, useEffect, useCallback from 'react';
import  useForm, Controller  from 'react-hook-form'
import  URL  from '../constants';

const UpdateUserData = props => 
    const [userData, setUserData] = useState(null);
    const  handleSubmit, control = useForm(mode: 'onBlur');

    const fetchUserData = useCallback(async account => 
        const userData = await fetch(`$URL/user/$account`)
                            .then(res=> res.json());
        console.log(userData);
        setUserData(userData);
    , []);

    useEffect(() => 
        const account = localStorage.getItem('account');
        fetchUserData(account);
    , [fetchUserData])

    const onSubmit = async (data) => 
        // TODO
    

    return (
        <div>
            <form onSubmit=handleSubmit(onSubmit)>
                <div>
                    <label>User Name:</label>
                    <Controller
                        as=<input type='text' />
                        control=control
                        defaultValue=userData ? userData.name : ''
                        name='name'
                    />
                </div>
                
                <div>
                    <label>Phone:</label>
                    <Controller
                        as=<input type='text' />
                        control=control
                        defaultValue=userData ? userData.phone : ''
                        name='phone'
                    />
                </div>
                <button>Submit</button>
            </form>
        </div>
    );


export default UpdateUserData;

调用的API运行良好,值实际上设置为userData状态。


  name: "John",
  phone: "02-98541566"
  ...

我还尝试在useEffect() 中使用模拟数据setUserData,但它也不起作用。 我上面的代码有什么问题吗?

【问题讨论】:

对不起,我没明白你想达到什么目的,你能解释一下吗? 【参考方案1】:

您可以使用 setValue (https://react-hook-form.com/api/useform/setvalue)。

从 useForm 导入:

const  handleSubmit, control, setValue = useForm( mode: 'onBlur' );

然后在收到用户数据后调用它:

useEffect(() => 
    if (userData) 
        setValue([
             name: userData.name , 
             phone: userData.phone 
        ]);
    
, [userData]);

您可以从表单中删除默认值。

编辑:如果这不起作用,请参阅下面的替代答案。

【讨论】:

一次为所有 FormValues 寻找 setValue 的 typescript 版本 @tommcandrew 你说得对,但我们仍然需要在表单中添加 defaultValues。 setValue 如何用于复选框和单选按钮? 对我不起作用。得到路径拆分错误。宁可回答下面的工作。【参考方案2】:

setValue 对我不起作用。或者,您可以使用reset 方法:

重置整个表单状态或部分表单状态。

这是工作代码:

 /* registered address */
const [registeredAddresses, setRegisteredAddresses] = useState([]);

const  register, errors, handleSubmit, reset  = useForm <FormProps> (
    validationSchema: LoginSchema,
);

/**
 * get addresses data
 */
const getRegisteredAddresses = async () => 
    try 
        const addresses = await AddressService.getAllAddress();
        setRegisteredAddresses(addresses);
        setDataFetching(false);
     catch (error) 
        setDataFetching(false);
    
;

useEffect(() => 
    getRegisteredAddresses();
, []);

useEffect(() => 
    if (registeredAddresses) 
        reset(
            addressName: registeredAddresses[0].name,
            tel: registeredAddresses[0].contactNumber
        );
    
, [registeredAddresses]); 

【讨论】:

【参考方案3】:

@tommcandrew 的 setValue 参数格式对我不起作用。

这种格式做到了:

useEffect(() => 
  const object = localStorage.getItem('object');
  setValue("name", object.name);
, [])

【讨论】:

这适用于我的 React Native 和控制器包装器。 终于。我做到了。谢谢兄弟【参考方案4】:

虽然这篇文章已经发布了 2 个月,但我今天偶然发现了这个问题,并寻找了几种方法来解决这个问题。我想出的最有效的方法是使用 useMemo 来设置你的 defaultValues,像这样:

const  control, errors, handleSubmit  = useForm(
    reValidateMode: 'onChange',
    defaultValues: useMemo(() => yourDefaultValues, [yourDefaultValues]),
);

这使您可以在表单中正确设置值,如果您碰巧有字段数组(这是我的情况),则无需进行多种实现。

这也适用于使用官方文档中的advanced smart form component exemple。如果您有任何问题,请告诉我!

【讨论】:

我在 6.8.3 上运行,每次yourDefaultValues 更改时都会重新评估默认值,但表单上没有任何反应(我在 useMemo 中添加了调试语句)。我已验证该名称与组件的name 相同,并且组件使用inputRefregister。你有什么想法吗? 复制链接:codesandbox.io/s/usereacthookformdefaultvalue-h6d71?file=/src/…【参考方案5】:

@tam 的答案已经完成了使其与 6.8.3 版一起工作所需的一半。

你需要提供默认值还要使用Effect来重置。如果您有一个与另一个实体一起重新加载的表单,则需要这种特殊的区别。我在CodeSanbox here 中有一个完整的例子。

简而言之: 您需要在 userForm 中定义您的 defaultValues。

 const  register, reset, handleSubmit  = useForm(
    defaultValues: useMemo(() => 
      return props.user;
    , [props])
  );

那么你需要倾听潜在的变化。

  useEffect(() => 
    reset(props.user);
  , [props.user]);

代码沙箱中的示例允许在两个用户之间进行交换并让表单更改其值。

【讨论】:

这绝对是这里最干净的答案之一。 @帕特里克,这行得通。只是一个疑问,当我尝试不包装 useMemo 时它也起作用了,我们使用 useMemo 的原因是它不会在下一次渲染时重新计算,除非任何依赖项发生变化。还有任何其他使用 useMemo 且不使用 useMemo 直接传递对象的差异,因为 useEffect 已经存在?如果这个错误你能纠正我吗?新反应:) 只是好奇你为什么在这里使用 useMemo? 这是正确的。【参考方案6】:

这适用于嵌套对象(我使用的是 6.15.1 版)

useEffect(() => 
    for (const [key, value] of Object.entries(data)) 
        setValue(key, value, 
            shouldValidate: true,
            shouldDirty: true
        )
    
, [data])

【讨论】:

最干净的实现 imo 虽然,eslint 不喜欢 for of 循环。一个可能的解决方案是Object.keys(data).forEach((val, i) =&gt; )【参考方案7】:

找到另一种简单的方法,我使用了 useForm 中的reset API

 const  handleSubmit, register, reset  = useForm( resolver );

调用API取回响应数据后,用新的apiData调用reset,确保apiData键与输入键相同(名称属性):

 useEffect(() => 
    reset(apiData);
  , [apiData]);

表单的默认值被缓存,因此一旦您从 API 获取数据,我们就会使用新数据重置表单状态。

【讨论】:

以上是关于如何使用 useEffect() 更改 React-Hook-Form defaultValue?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 React useEffect 中更改路由器

如何使用/不使用 useEffect 钩子在 React JS 函数中获取更改的(新)状态值?

如何停止 React.useEffect 的副作用? [复制]

数据更改后停止执行函数(React,useEffect)

无法在 React 中更改 useEffect 中的变量 [重复]

每次更改页面时都会调用 React useEffect (with []) (React Router)