Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

In my simple crud application, when I try to add a new author with an invalid name format and try to submit form display an error and after that when I have press backspace twice to erase last letter in textbox.

Here is my 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-Zs]+$/
                                          })}
                                          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;

And this is 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;

Here is the sandbox link to my full code Click Here

to demonstrate the error,

  1. go to the sandbox link
  2. click add author button in webapp
  3. enter an invalid name like 'john97'
  4. then submit the form
  5. then try to clear the name using backspace.
  6. now you can see to erase last letter 'j' you have to press backspace twice

please help me to solve this issue

thank you

question from:https://stackoverflow.com/questions/66054594/react-hooks-form-onchange-misbehave-after-error-validation-occured

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
185 views
Welcome To Ask or Share your Answers For Others

1 Answer

I think using Controller component of react-hook-form is better at handling controlled components, you also don't have to set onChange event and make your code much cleaner.

Using this in AuthorForm.tsx seems to make your weird bug fixed.

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
   }}
/>

Here is the sandbox.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...