使用 React Hook Form 无法编辑字段

Posted

技术标签:

【中文标题】使用 React Hook Form 无法编辑字段【英文标题】:Fields are not editable using React Hook Form 【发布时间】:2021-04-09 13:31:19 【问题描述】:

我有这个 UserEditScreen.js 文件,我通过 React-Redux 从 mongodb 提取所有数据。

我需要进行一些验证,所以我使用了React Hook Form 来执行此操作。所以在我的代码上:

import React,  useState, useEffect  from 'react'
import  useForm  from 'react-hook-form'
import  useDispatch, useSelector  from 'react-redux'
import  Message from '../components/Message'
import  Loader from '../components/Loader'
import  getUserDetails, updateUser  from '../actions/userActions'
import  USER_UPDATE_RESET  from '../constants/userConstants'

const UserEditScreen = ( match, history ) => 

    const  watch, register, errors, handleSubmit  = useForm()
    console.log('role')

    const userId = match.params.id

    // const [name, setName] = useState('')
    // const [email, setEmail] = useState('')
    // const [phone, setPhone] = useState('')
    // const [role, setRole] = useState('')

    const dispatch = useDispatch()

    const userDetails = useSelector(state => state.userDetails)
    const  loading, error, user  = userDetails 

    const userUpdate = useSelector(state => state.userUpdate)
    const  loading:loadingUpdate, error:errorUpdate, success:successUpdate  = userUpdate 

     useEffect(() => 

       if(successUpdate)
           dispatch( type: USER_UPDATE_RESET )
           history.push('/admin/userslist')
        else 
        if(!user.name || user._id !== userId)
            dispatch(getUserDetails(userId))
        
        // else 
        //      setName(user.name)
        //      setEmail(user.email)
        //      setPhone(user.phone)
        //      setRole(user.role)
        //  
       
     , [dispatch, history, userId, user, successUpdate])

    const submitHandler = (data, e) => 
        e.preventDefault()
        const name, email, phone, role  = data
        dispatch(updateUser( _id: userId, name, email, phone, role  ))
    

    return ( 
        <>
            <h1>Edit User</h1>
             loadingUpdate && <Loader /> 
     errorUpdate && <Message variant='danger'>errorUpdate</Message>
     loading ? <Loader /> : error ? <Message variant='danger'>error</Message> : (
            <form onSubmit=submitHandler> 
            <div className="form-group"> 
                <label for="name">Name</label>
                        <input
                            type="text"
                            name="name"
                            value=user.name
                            className=`form-control $errors.name ? 'is-invalid' : ''`
                            id="name"
                            ref=register( required: true, minLength: 2, maxLength: 30 )
                        />
                         errors.name && errors.name.type ==='required' && <p className="text-danger">Name is required.</p> 
                         errors.name && errors.name.type ==='minLength' && <p className="text-danger">Name is too short.</p> 
                         errors.name && errors.name.type ==='maxLength' && <p className="text-danger">Name is exceeds maximum length.</p> 
                    </div>
                    
            <div className="form-group"> 
                <label for="email">Email address</label>
                        <input
                            type="email"
                            name="email"
                            value=user.email
                            className=`form-control $errors.email ? 'is-invalid' : ''`
                            id="email"
                            ref=register( required: true, minLength: 8, maxLength: 30, pattern: /^\S+@\S+\.\S+$/ )
                        />
                         errors.email && errors.email.type ==='required' && <p className="text-danger">Email is required.</p> 
                         errors.email && errors.email.type ==='minLength' && <p className="text-danger">Email length is too small.</p> 
                         errors.email && errors.email.type ==='maxLength' && <p className="text-danger">Email exceeds maximum length.</p> 
                         errors.email && errors.email.type ==='pattern' && <p className="text-danger">That is not a valid email.</p> 
            </div>
            <div className="form-group"> 
                <label for="phone">Phone</label>
                        <input
                            type="text"
                            value=user.phone
                            className=`form-control $errors.phone ? 'is-invalid' : ''`
                            id="phone"
                            ref=register( required: true, minLength: 10, maxLength: 13, pattern: /^(\+\d1,2\s)?\(?\d3\)?[\s.-]\d3[\s.-]\d4$/)
                        />
                         errors.phone && errors.phone.type ==='required' && <p className="text-danger">Phone is required.</p> 
                         errors.phone && errors.phone.type ==='minLength' && <p className="text-danger">Phone length is too small.</p> 
                         errors.phone && errors.phone.type ==='maxLength' && <p className="text-danger">Phone exceeds maximum length.</p> 
                         errors.phone && errors.phone.type ==='pattern' && <p className="text-danger">Phone is not a valid phone.</p> 
            </div>
            <div className="form-group"> 
                <label for="phone">User Role</label>
                        <div class="role">
                        <input 
                   type="radio" 
                   className="btn-check" 
                   name="role" 
                   id="administrator" 
                   autocomplete="off"  
                   value='administrator'
                   ref=register( required: true )
                   checked=user.role === 'administrator'
                    />
                <label className="btn btn-outline-success" for="administrator">Administrator</label>
                <input 
                   type="radio" 
                   className="btn-check" 
                   name="role" 
                   id="resort-owner" 
                   autocomplete="off"  
                   value='resortOwner'
                    ref=register( required: true )
                    checked=user.role === 'resortOwner'
                    />
                <label className="btn btn-outline-success" for="resort-owner">Resort Owner</label>
                <input 
                   type="radio" 
                   className="btn-check"
                   name="role" 
                   id="reviewer" 
                   autocomplete="off" 
                   value='reviewer'
                    ref=register( required: true )
                    checked=user.role === 'reviewer'
                />
                <label className="btn btn-outline-success" for="reviewer">Reviewer</label>
                 errors.role && errors.role.type ==='required' && <p className="text-danger">Choose a role.</p> 
             </div>

            </div>
        
            <button type="submit" className="btn btn-primary">Update</button>
            </form>
            
    )

        </>
    )


export default UserEditScreen

如您所见,我注释掉了useState 字段并尝试通过user.nameuser.email 等直接从userDetails 操作中提取数据。我通过每个输入字段值属性设置了这些数据。

但是,执行此操作后,我无法再修改或编辑任何字段。我需要设置一些验证,但希望仍然能够编辑每个字段并使用 react hook 表单提交表单,但我不知道如何解决这个问题。

知道如何解决这个问题并使表单字段可编辑以及能够使用反应钩子表单提交带有验证的表单吗?请帮忙。

更新:我尝试使用defaultValue 而不是value,因此它适用于姓名、电子邮件、电话,但现在我无法选择或修改单选按钮role 的值一。这是我更新的代码。

const UserEditScreen = ( match, history ) => 

const  watch, register, errors, handleSubmit  = useForm()
console.log(watch('name'))
console.log(watch('email'))
console.log(watch('phone'))
console.log(watch('role'))

const userId = match.params.id

// const [name, setName] = useState('')
// const [email, setEmail] = useState('')
// const [phone, setPhone] = useState('')
// const [role, setRole] = useState('')

const dispatch = useDispatch()

const userDetails = useSelector(state => state.userDetails)
const  loading, error, user  = userDetails 

const userUpdate = useSelector(state => state.userUpdate)
const  loading:loadingUpdate, error:errorUpdate, success:successUpdate  = userUpdate 

 useEffect(() => 

   if(successUpdate)
       dispatch( type: USER_UPDATE_RESET )
       history.push('/admin/userslist')
    else 
    if(!user.name || user._id !== userId)
        dispatch(getUserDetails(userId))
    

    // else 
    //      setName(user.name)
    //      setEmail(user.email)
    //      setPhone(user.phone)
    //      setRole(user.role)
    //  
   
 , [dispatch, history, userId, user, successUpdate,])

const submitHandler = (data, e) => 
    e.preventDefault()
    const name, email, phone, role  = data
    dispatch(updateUser( _id: userId, name, email, phone, role  ))


return ( 
    <>
        <h1>Edit User</h1>
         loadingUpdate && <Loader /> 
 errorUpdate && <Message variant='danger'>errorUpdate</Message>
 loading ? <Loader /> : error ? <Message variant='danger'>error</Message> : (
        <form onSubmit=handleSubmit(submitHandler)> 
        <div className="form-group"> 
            <label for="name">Name</label>
                    <input
                        type="text"
                        name="name"
                        defaultValue=user.name
                        className=`form-control $errors.name ? 'is-invalid' : ''`
                        id="name"
                        ref=register( required: true, minLength: 2, maxLength: 30 )
                    />
                     errors.name && errors.name.type ==='required' && <p className="text-danger">Name is required.</p> 
                     errors.name && errors.name.type ==='minLength' && <p className="text-danger">Name is too short.</p> 
                     errors.name && errors.name.type ==='maxLength' && <p className="text-danger">Name is exceeds maximum length.</p> 
                </div>
                
        <div className="form-group"> 
            <label for="email">Email address</label>
                    <input
                        type="email"
                        name="email"
                        defaultValue=user.email
                        className=`form-control $errors.email ? 'is-invalid' : ''`
                        id="email"
                        ref=register( required: true, minLength: 8, maxLength: 30, pattern: /^\S+@\S+\.\S+$/ )
                    />
                     errors.email && errors.email.type ==='required' && <p className="text-danger">Email is required.</p> 
                     errors.email && errors.email.type ==='minLength' && <p className="text-danger">Email length is too small.</p> 
                     errors.email && errors.email.type ==='maxLength' && <p className="text-danger">Email exceeds maximum length.</p> 
                     errors.email && errors.email.type ==='pattern' && <p className="text-danger">That is not a valid email.</p> 
        </div>
        <div className="form-group"> 
            <label for="phone">Phone</label>
                    <input
                        type="text"
                        name="phone"
                        defaultValue=user.phone
                        className=`form-control $errors.phone ? 'is-invalid' : ''`
                        id="phone"
                        ref=register( required: true, minLength: 10, maxLength: 13 )
                    />
                     errors.phone && errors.phone.type ==='required' && <p className="text-danger">Phone is required.</p> 
                     errors.phone && errors.phone.type ==='minLength' && <p className="text-danger">Phone length is too small.</p> 
                     errors.phone && errors.phone.type ==='maxLength' && <p className="text-danger">Phone exceeds maximum length.</p> 
                     errors.phone && errors.phone.type ==='pattern' && <p className="text-danger">Phone is not a valid phone.</p> 
        </div>
        <div className="form-group"> 
            <label for="phone">User Role</label>
                    <div class="role">
                    <input 
               type="radio" 
               className="btn-check" 
               name="role" 
               id="administrator" 
               autocomplete="off"  
               value='administrator'
               ref=register( required: true )
               checked=user.role === 'administrator'
                />
            <label className="btn btn-outline-success" for="administrator">Administrator</label>
            <input 
               type="radio" 
               className="btn-check" 
               name="role" 
               id="resort-owner" 
               autocomplete="off"  
               value='resortOwner'
                ref=register( required: true )
                checked=user.role === 'resortOwner'
                />
            <label className="btn btn-outline-success" for="resort-owner">Resort Owner</label>
            <input 
               type="radio" 
               className="btn-check"
               name="role" 
               id="reviewer" 
               autocomplete="off" 
               value='reviewer'
                ref=register( required: true )
                checked=user.role === 'reviewer'
            />
            <label className="btn btn-outline-success" for="reviewer">Reviewer</label>
             errors.role && errors.role.type ==='required' && <p className="text-danger">Choose a role.</p> 
         </div>

        </div>
    
        <button type="submit" className="btn btn-primary">Update</button>
        </form>
        
)

    </>
)

【问题讨论】:

你试过用defaultValue代替value吗? 我做了,但它不会观看或编辑phonerole(单选按钮)有什么想法吗? 我尝试在 phonerole 上使用 watch 来控制台记录它们,但我无法更改或修改它们。 更新:我尝试使用 defaultValue 而不是 value,因此它适用于姓名、电子邮件、电话,但现在我无法选择或修改单选按钮一的角色值。更新了上面的代码。 【参考方案1】:

不要在inputs 上使用value 属性。如果你也没有为 onChange 提供值,React 将使表单控件无法编辑。改为使用reset钩子方法异步设置字段值:

const  watch, register, errors, handleSubmit, reset  = useForm();
...
useEffect(() => 
  reset(
    name: user.name,
    email: user.email,
    ...
  );
, [reset, user]);

阅读React docs on controlled form components

见example on codesandbox.io

【讨论】:

不确定,但它没有拿起电话和角色。我在上面更新了我的问题。此外,我正在通过 redux FYI 获取数据库上的一些数据,而不仅仅是一个常规的创建表单,它是一个编辑表单 更新:我尝试使用 defaultValue 而不是 value,因此它适用于姓名、电子邮件、电话,但现在我无法选择或修改单选按钮一的角色值。更新了上面的代码。 不要在inputs 上使用valuedefaultValue 属性。看看 react form 钩子代码示例,它们可以向您展示做您需要的正确方法:github.com/react-hook-form/react-hook-form/tree/master/examples【参考方案2】:

我认为您必须尝试使用​​onChange 或其他侦听器来输入并从侦听器回调中设置内部值。内在价值是你的状态。通常 React 用于受控输入,onChange 上的设置值会用新值重新渲染这个组件,并且可以编辑。试试;)

【讨论】:

React Hook Form 设计为与不受控制的输入字段一起使用作为默认值,因此这不是 OPs 问题的好答案。 我同意。我尝试了他所说的,但根本没有用。谢谢马克。

以上是关于使用 React Hook Form 无法编辑字段的主要内容,如果未能解决你的问题,请参考以下文章

如何使 react-hook-form 在一页中使用多个表单?

文本字段更改的值未在 OnSubmit 中更新 - React-Hook-Form 和 React Js

react-hook-form 的 DefaultValues 未将值设置为 React JS 中的输入字段

react-hook-form 在组件中使用以传递动态文本字段的道具

在 react-hook-form 的 useFieldArray 上异步调用 reset 时,reset 无法正常工作

需要 React Hook Form 动态