【问题标题】:React hooks form onChange misbehave after error validation occured发生错误验证后反应钩子形成 onChange 行为不端
【发布时间】:2021-02-04 22:10:49
【问题描述】:

在我简单的 crud 应用程序中,当我尝试添加一个名称格式无效的新作者并尝试提交表单时显示错误,然后当我按两次退格键删除文本框中的最后一个字母时。

这是我的 AuthorForm.tsx

import React, {useEffect, useState} from 'react';
import {Row, Col, Form, Button} from 'react-bootstrap';
import {IAuthor} from "../../assets/types/LibraryTypes";
import {XCircle} from "react-feather";
import {useForm} from "react-hook-form";

interface IFormInputs {
    authorName: string
}

type CreateFormProps = {
    onClose: () => void,
    onAuthorAdded: (author:IAuthor)=>void,
    onAuthorToUpdate: IAuthor | null,
    updateAuthorIndex : number | null,
    onAuthorUpdated : (updatedAuthor:IAuthor, index:number)=>void,
}

const AuthorForm:React.FC<CreateFormProps> = (props) =>{

    const [authorName, setAuthorName] = useState<string|null>(null);
    const { register, errors, handleSubmit } = useForm<IFormInputs>({mode:'onTouched'});

    useEffect(()=>{
        if(!props.onAuthorToUpdate){
            setAuthorName(null);
            return;
        }
        setAuthorName(props.onAuthorToUpdate.name);
    },[props.onAuthorToUpdate]);

    const handleOnNameChange = (event:React.ChangeEvent<HTMLInputElement>)=>{
        event.preventDefault();
        setAuthorName(event.target.value);
    }
    const handleOnCreate = () =>{
        if(!authorName){
            return;
        }

        if(props.onAuthorToUpdate && props.updateAuthorIndex !== null){
            props.onAuthorUpdated({...props.onAuthorToUpdate,name:authorName},props.updateAuthorIndex);
            setAuthorName(null);
            return;
        }

        const newAuthor: IAuthor = {name:authorName};
        props.onAuthorAdded(newAuthor);
        setAuthorName(null);
    };


    return(
        <Col className='p-0' sm={10}>
            <Row className=' pb-1 mb-3 mx-1'>
                <Col xs={10}>
                    <span className='add-book-title pt-2'>
                        {!props.onAuthorToUpdate && 'Create Author'}
                        {props.onAuthorToUpdate && 'Update Author'}
                    </span>
                </Col>
                <Col className='closeBtn text-right p-0' xs={2}>
                    <XCircle color='#363636' className='mt-2 mr-3' onClick={props.onClose}/>
                </Col>
            </Row>
            <Form className='mx-4' onSubmit={handleSubmit(handleOnCreate)}>
                <Form.Group>
                    <Form.Row>
                        <Form.Label column="sm" xs={6} className='label'>
                            Name of the Author
                        </Form.Label>
                        <Col xs={6} className='warning text-right mt-2 pr-2'>
                            {errors.authorName?.type === "required" && (
                                <p>This field is required</p>
                            )}
                            {errors.authorName?.type === "maxLength" && (
                                <p>Author Name name cannot exceed 50 characters</p>
                            )}
                            {errors.authorName?.type === "pattern" && (
                                <p>Invalid Author Name</p>
                            )}
                        </Col>
                        <Col sm={12}>
                            <Form.Control size={"sm"}
                                          name="authorName"
                                          ref={register({
                                              required: true,
                                              maxLength: 50,
                                              pattern: /^[a-zA-Z\s]+$/
                                          })}
                                          onChange={
                                              (event:React.ChangeEvent<HTMLInputElement>)=>
                                                  handleOnNameChange(event)
                                          }
                                          value={authorName?authorName:''}

                            />

                        </Col>
                    </Form.Row>
                </Form.Group>
                    <Col className='text-right mb-3 p-0' xs={12}>
                        <Button type={"submit"} variant={"primary"} size={"sm"} className={"px-3 pt-1"}>
                            {!props.onAuthorToUpdate && 'Create'}
                            {props.onAuthorToUpdate && 'Update'}
                        </Button>
                    </Col>
            </Form>
        </Col>
    )
};

export default AuthorForm;

这是 AuthorList.tsx

import React, {useEffect, useState} from 'react';
import {Container} from 'react-bootstrap';
import AuthorAddedList from "./AuthorAddedList";
import AuthorForm from "./AuthorForm";
import AuthorWelcome from "./AuthorWelcome";
import CreateAuthor from "./CreateAuthor";
import {IAuthor} from "../../assets/types/LibraryTypes";


const AuthorList:React.FC = () =>{

        const initAuthors: IAuthor[] = [];
        const [authors, setAuthors] = useState<IAuthor[]>(initAuthors);
        const [isFormVisible, setIsFormVisible] = useState<boolean>(false);
        const [authorToUpdate, setAuthorToUpdate] = useState<IAuthor | null>(null);
        const [updateAuthorIndex, setUpdateAuthorIndex] = useState<number| null>(null)

        useEffect(()=>{
            if(!authorToUpdate){
                return;
            }
            setIsFormVisible(true);
        },[authorToUpdate]);

        const handleOnCreateClick = () => {
            setIsFormVisible(true);
            setAuthorToUpdate(null);
        };


        const handleOnFormClosed = () => {
            setIsFormVisible(false);
        }

        const handleAuthorAdded = (newAuthor: IAuthor) => {
            const allAuthors: IAuthor[] = authors.slice();
            allAuthors.push(newAuthor)
            setAuthors(allAuthors);
        };

        const handleAuthorDeleted = (index: number) => {
            const allAuthors: IAuthor[] = authors.slice();
            allAuthors.splice(index, 1);
            setAuthors(allAuthors);
        }

        const handleOnUpdateRequest = (index: number) => {
            setAuthorToUpdate(authors[index]);
            setUpdateAuthorIndex(index);
            setIsFormVisible(true);
        }

        const handleOnAuthorUpdated = (updatedAuthor: IAuthor, index:number) =>{
            const allAuthors : IAuthor [] = authors.slice();
            allAuthors.splice(index,1, updatedAuthor);
            setAuthors(allAuthors)
        }

        return (

            <Container fluid={true} className={"authors"}>
                <AuthorWelcome/>
                <AuthorAddedList authors={authors} onDeleted={handleAuthorDeleted} onUpdateRequested={handleOnUpdateRequest} />
                <CreateAuthor onClickCreate={handleOnCreateClick}/>
                {isFormVisible &&
                <AuthorForm onClose={handleOnFormClosed} onAuthorAdded={handleAuthorAdded} onAuthorToUpdate={authorToUpdate} onAuthorUpdated={handleOnAuthorUpdated} updateAuthorIndex={updateAuthorIndex}/>}
            </Container>
        )
    }

export default AuthorList;

这是我完整代码Click Here的沙盒链接

演示错误,

  1. 转到沙盒链接
  2. 点击 webapp 中的添加作者按钮
  3. 输入无效名称,例如“john97”
  4. 然后提交表单
  5. 然后尝试使用退格键清除名称。
  6. 现在您可以看到要删除最后一个字母“j”,您必须按两次退格键

请帮我解决这个问题

谢谢

【问题讨论】:

    标签: reactjs typescript react-hooks react-hook-form


    【解决方案1】:

    我认为使用react-hook-formController 组件可以更好地处理受控组件,您也不必设置 onChange 事件并使代码更清晰。

    AuthorForm.tsx 中使用它似乎可以修复你奇怪的错误。

    type FormData = {
      authorName: string;
    }
    
    //some codes...
    const { register, handleSubmit, control, errors, setValue, reset } = useForm<FormData>();
    
    //some codes...
    const handleOnCreate = (data: FormData) => {
        if (!data?.authorName) {
          return;
        }
    
        if (props.onAuthorToUpdate && props.updateAuthorIndex !== null) {
          props.onAuthorUpdated(
            { ...props.onAuthorToUpdate, name: data.authorName },
            props.updateAuthorIndex
          );
          reset({ authorName: "" }); // or setValue("authorName", "");
          return;
        }
    
        const newAuthor: IAuthor = { name: data.authorName };
        props.onAuthorAdded(newAuthor);
        reset({ authorName: "" }); // or setValue("authorName", "");
      };
    
    
    //some codes...
    <Controller
       control={control}
       name={"authorName"}
       as={<Form.Control size={"sm"} />}
       defaultValue=""
       rules={{
         required: true,
         maxLength: 50,
         pattern: /^[A-Za-z ]+$/i
       }}
    />
    

    这里是sandbox

    【讨论】:

    • 当点击编辑名称并打开更新表单时,名称文本框中没有显示当前名称
    • 由于我们不再使用authorName 状态,您可以将useEffect 中的代码替换为useForm's setValues 以在文本框中设置值。 SandBox
    • 提交表单后,表单没有重置我已经添加了setValue("authorName",null) doesn't work
    • 我忘记在handleOnCreate 中添加重置,我已经编辑了我的答案。
    • 我也觉得奇怪的是setValue 不适合你,它似乎不喜欢空值,当我尝试setValue("authorName", ""); 时,它工作正常。
    【解决方案2】:

    问题出在AuthorForm.tsx文件的这一行:

    <Form className='mx-4' onSubmit={handleSubmit(handleOnCreate)}>
    

    onSubmit 不应接受调用的函数handleSubmit(handleOnCreate), 应该改成onSubmit={() =&gt; handleSubmit(handleOnCreate)}

    【讨论】:

    • 添加新作者时,它会重置组件并列表为空
    猜你喜欢
    • 1970-01-01
    • 2021-03-23
    • 1970-01-01
    • 1970-01-01
    • 2021-07-07
    • 1970-01-01
    • 2022-01-25
    • 2021-01-19
    • 2021-01-11
    相关资源
    最近更新 更多