Reputation: 61
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-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;
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,
please help me to solve this issue
thank you
Upvotes: 1
Views: 2846
Reputation: 163
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.
Upvotes: 1
Reputation: 649
the problem is in the AuthorForm.tsx
file, in this line:
<Form className='mx-4' onSubmit={handleSubmit(handleOnCreate)}>
the onSubmit
shouldn't accept an invoked function handleSubmit(handleOnCreate)
,
it should be changed to onSubmit={() => handleSubmit(handleOnCreate)}
Upvotes: 0