发生错误验证后,反应钩子表单 onChange 行为异常

时间:2021-02-04 22:10:49

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

在我的简单 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”,您必须按两次退格键

请帮我解决这个问题

谢谢

2 个答案:

答案 0 :(得分:1)

我认为使用 Controllerreact-hook-form 组件可以更好地处理受控组件,您也不必设置 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

答案 1 :(得分:0)

问题出在 println(s"matches ${pattern.matches("7,2323")}") 文件中,在这一行:

AuthorForm.tsx

<Form className='mx-4' onSubmit={handleSubmit(handleOnCreate)}> 不应接受调用的函数 onSubmit, 应该改为handleSubmit(handleOnCreate)