I am working on yup validation. Whenever it comes to FileList validation, if I left the input blank, I got the following error: enter image description here
Here is my code:
import React, { useEffect, useMemo, useState } from 'react';
import { useLocation } from 'react-router';
import { useDispatch, useSelector } from '@app/hooks';
import { Button, Dimmer, Dropdown, DropdownItemProps, DropdownProps, Form, Header, Loader, Modal } from 'semantic-ui-react';
import { NewBook, TestBook, UpdateParam } from '@app/reducers/master-data/book/book.model';
import * as yup from "yup";
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { useHistory } from 'react-router-dom';
import { _getAllGenres } from '@app/reducers/master-data/genre/genre.slice';
import { _getAllPublisher } from '@app/reducers/master-data/publisher/publisher.slice';
import { _getAllAuthors } from '@app/reducers/master-data/author/author.slice';
import { _getAllDepartment } from '@app/reducers/master-data/department/department.slice';
import { _updateBook, _getAllBooks, _getBookDetail, _uploadBookFile } from '@app/reducers/master-data/book/book.slice';
import { Wrapper } from '@app/components/data-table/StyledComponent';
import { IBookParam } from '@app/reducers/master-data/book/book.service';
interface IProps {
}
interface ErrMessage {
type: string,
message: string
}
const schema = yup.object().shape({
title: yup.string().required('Title cannot blank'),
publishYear: yup.number(),
isPublic: yup.boolean(),
language: yup.string(),
authorIDs: yup.array(),
genreIDs: yup.array(),
description: yup.string(),
// departmentIDs: yup.array(),
publisherID: yup.number(),
file: yup
.mixed()
.required("A random message")
.nullable(false)
.test("Empty file", "You need to provide a file", (value: FileList) => {
let x = value.length !== 0
debugger
return value.length !== 0;
})
.test("type", "Only support PDF file", (value: FileList) => {
return value[0].type === "application/pdf"
})
});
const UpdateBookPage: React.FC<IProps> = (props) => {
const dispath = useDispatch()
const history = useHistory();
useEffect(() => {
dispath(_getAllGenres());
dispath(_getAllPublisher());
dispath(_getAllAuthors());
dispath(_getAllDepartment());
// dispath(_getAllBooks());
dispath(_getBookDetail(id));
}, [])
const search = useLocation().search;
const query = new URLSearchParams(search);
const id = query.get('id') || '';
const genres = useSelector(state => state.genre).genres;
const publishers = useSelector(state => state.publisher).publishers;
const authors = useSelector(state => state.author).authors;
const departments = useSelector(state => state.department).departments;
const book = useSelector(state => state.book.selectedBook);
const isLoading = useSelector(state => state.book.isLoading);
const statusCode = useSelector(state => state.book.statusCode);
// console.log(book)
const defaultDepartments = useMemo<number[]>(() => {
let temp: number[] = []
for (let x of book.departments) {
temp.push(x.id)
};
return temp;
}, [book.departments])
const defaultAuthors = useMemo<number[]>(() => {
let temp: number[] = []
for (let x of book.authors) {
temp.push(Number(x.id))
};
return temp;
}, [book.authors])
const defaultGenres = useMemo<number[]>(() => {
let temp: number[] = []
for (let x of book.genres) {
temp.push(Number(x.id))
};
return temp;
}, [book.genres])
const departmentOptions = useMemo<DropdownItemProps[]>(() => {
let temp: DropdownItemProps[] = []
for (let x of departments) {
temp.push({
key: x.id,
text: x.name,
value: x.id
})
};
return temp;
}, [departments])
const genreOptions = useMemo<DropdownItemProps[]>(() => {
let temp: DropdownItemProps[] = []
for (let x of genres) {
temp.push({
key: x.id,
text: x.name,
value: x.id
})
};
return temp;
}, [genres])
const authorOptions = useMemo<DropdownItemProps[]>(() => {
let temp: DropdownItemProps[] = []
for (let x of authors) {
temp.push({
key: x.id,
text: x.fullName,
value: x.id
})
};
return temp;
}, [authors])
const [openForm, setOpenForm] = useState<boolean>(false)
const [authorSelected, setAuthorSelected] = useState<Number[]>([...defaultAuthors]);
const [genreSelected, setGenreSelected] = useState<Number[]>([...defaultGenres]);
const [departmentSelected, setDepartmentSelected] = useState<Number[]>([...defaultDepartments]);
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [fileErrorText, setFileErrorText] = useState<string>('');
// const [selectedFile, setSelectedFile] = useState<File|null>(null);
const [isOpen, setIsOpen] = useState<boolean>(false);
const [alertText, setAlertText] = useState<string>('');
const [isFailed, setIsFailed] = useState<boolean>(false);
const [errMessage, setErrMessage] = useState<ErrMessage>({
type: '',
message: ''
})
const { register, handleSubmit, formState: { errors }, setValue, trigger } = useForm<TestBook>({
resolver: yupResolver(schema),
// defaultValues: defaultBook
});
useEffect(() => {
if (book) {
setInitValue()
}
}, [book])
const setInitValue = () => {
setValue('title', book.title)
setValue('publishYear', book.publishYear)
setValue('authorIDs', [...defaultAuthors])
setValue('language', 'vi')
setValue('departmentIDs', [...defaultDepartments])
setValue('description', book.description)
setValue('genreIDs', [...defaultGenres])
setValue('isPublic', book.isPublic)
setValue('publisherID', Number(book.publisher.id))
}
// console.log(defaultAuthors, defaultDepartments, defaultGenres)
const handleSubmitBtn = async (data: NewBook) => {
debugger
console.log(data);
const valid = validateData();
debugger
if (valid) {
data.authorIDs = authorSelected.concat(defaultAuthors);
data.genreIDs = genreSelected.concat(defaultGenres)
data.departmentIDs = departmentSelected.concat(defaultDepartments)
const updateParam: UpdateParam = {
id: id,
newBook: data
}
console.log(data);
console.log(selectedFile);
try {
debugger
await dispath(_updateBook(updateParam)).then(() => {
if (statusCode == 200) {
if (selectedFile != null) {
let formData = new FormData();
formData.append("FILE", selectedFile);
formData.append("NAME", selectedFile.name);
const param: IBookParam = {
id: localStorage.getItem("BOOKID") || '',
file: formData
}
console.log(param.file);
console.log(param.id);
dispath(_uploadBookFile(param)).then(() => {
if (statusCode == 200) {
setIsOpen(true);
console.log(isOpen);
setAlertText("Update successfully")
} else {
setIsOpen(true)
setAlertText("An error has occured. Please try again later");
setIsFailed(true);
}
})
} else {
return;
}
}
console.log(data);
})
}
catch {
setIsOpen(true)
setAlertText("An error has occured. Please try again later");
setIsFailed(true);
}
}
}
const validateData = (): boolean => {
let x = authorSelected.concat(defaultAuthors)
let y = genreSelected.concat(defaultGenres)
let z = departmentSelected.concat(defaultDepartments)
debugger
if (authorSelected.concat(defaultAuthors).includes(0)) {
setErrMessage({
type: "AUTHOR",
message: "At least one author must be selected"
});
return false;
} else if (genreSelected.concat(defaultGenres).includes(0)) {
setErrMessage({
type: "GENRE",
message: "At least one genre must be selected",
});
return false;
} else if (departmentSelected.concat(defaultDepartments).includes(0)) {
setErrMessage({
type: "DEPARTMENT",
message: "At least one department must be selected",
});
return false;
}
return true;
}
const handleAddItem = (data: DropdownProps, type: string) => {
let val = JSON.stringify(data.value)
let numArr: Number[] = []
val = val.substring(1, val.length - 1)
let valArr = val.split(',');
// console.log(valArr);
for (let x of valArr) {
numArr.push(Number(x))
// console.log(numArr)
}
console.log(numArr)
// const y: Number[] = numArr
switch (type) {
case "AUTHOR":
setValue("authorIDs", numArr)
setAuthorSelected(numArr)
break;
case "GENRE":
setValue("genreIDs", numArr)
setGenreSelected(numArr);
break;
case "DEPARTMENT":
// if(numArr.length > 5){
// setErrMessage({
// type: "Department",
// message: "You can only select maximum of 5 departments"
// })
// break;
// }
setValue("departmentIDs", numArr)
setDepartmentSelected(numArr);
break;
}
}
const handleSelectFile = (event: any) => {
console.log(event.target.files[0]);
setSelectedFile(event.target.files[0]);
}
const handleCancelAction = () => {
history.push('/master-data/manage-book')
}
useEffect(() => {
console.log(authorSelected)
console.log(genreSelected)
console.log(departmentSelected)
}, [authorSelected, genreSelected, departmentSelected])
return (
<div className="form-create-book">
<Header>Update book</Header>
{
isLoading && (
<Wrapper style={{ padding: '40px 0' }}>
<Dimmer inverted active={true}>
<Loader>Loading</Loader>
</Dimmer>
</Wrapper>)
}
{
!isLoading && (
<div>
<Modal
size={"tiny"}
open={isOpen}
>
<Modal.Content>
<h2 style={{textAlign: 'center'}}>{alertText}</h2>
</Modal.Content>
<Modal.Actions>
{!isFailed &&
<Button negative onClick={() => setIsOpen(false)}>
Close
</Button>
}
<Button positive onClick={() => history.push("/librarian/master-data/manage-book")}>
Ok
</Button>
</Modal.Actions>
</Modal>
<Form className="create-book-form" onSubmit={handleSubmit(handleSubmitBtn)}>
<Form.Field>
<label className='dot-required'>Title</label>
<input {...register('title')} className="text-field" />
<p>{errors.title?.message}</p>
</Form.Field>
<Form.Field>
<label>Description</label>
<textarea {...register('description')} className="textarea-field" />
<p>{errors.description?.message}</p>
</Form.Field>
<Form.Field>
<label className='dot-required'>Publish Year</label>
<input className="text-field" {...register('publishYear')} />
<p>{errors.publishYear?.message}</p>
</Form.Field>
<Form.Field>
<label className='dot-required'>PDF File</label>
<input {...register('file')} className="text-field" accept="application/pdf" type="file" name="file" onChange={handleSelectFile} />
<p>{errors.file?.message}</p>
</Form.Field>
<Form.Field>
<label className='dot-required'>Access Permission</label>
<select defaultValue={book.isPublic ? 1 : 0} className="text-field" {...register('isPublic')}>
<option value={1}>Public</option>
<option value={0}>Restricted</option>
</select>
<p>{errors.isPublic?.message}</p>
</Form.Field>
<Form.Field>
<label className='dot-required'>Publisher</label>
<select className="text-field" {...register('publisherID')} >
{
publishers.map((e) => {
if (e.id === book.publisher.id)
return (<option selected value={e.id}>{e.name}</option>)
else
return (<option value={e.id}>{e.name}</option>)
}
)
}
</select>
<p>{errors.publisherID?.message}</p>
</Form.Field>
<Form.Field>
<label className='dot-required'>Authors</label>
<Dropdown className="text-field" onChange={(e, data) => handleAddItem(data, "AUTHOR")} placeholder='Authors' fluid multiple selection options={authorOptions} defaultValue={defaultAuthors} />
<p>{errMessage.type == "AUTHOR" ? errMessage.message : ''}</p>
</Form.Field>
<Form.Field>
<label className='dot-required'>Genre</label>
<Dropdown className="text-field" onChange={(e, data) => handleAddItem(data, "GENRE")} placeholder='Genres' fluid multiple selection options={genreOptions} defaultValue={defaultGenres} />
<p>{errMessage.type == "GENRE" ? errMessage.message : ''}</p>
</Form.Field>
<Form.Field>
<label className='dot-required'>Department</label>
<Dropdown className="text-field" onChange={(e, data) => handleAddItem(data, "DEPARTMENT")} placeholder='Genres' fluid multiple selection options={departmentOptions} defaultValue={defaultDepartments} />
<p>{errMessage.type == "DEPARTMENT" ? errMessage.message : ''}</p>
</Form.Field>
<Button
content="Submit"
// onClick={handleSubmit(handleSubmitBtn)}
className="ui inverted green button"
/>
<Button
color="black"
content="Cancel"
onClick={() => handleCancelAction()}
className="negative ui button"
/>
</Form>
</div>
)
}
</div>
);
};
export default UpdateBookPage;
When I check the source tab, I find out that the problem comes from here:
But I don't know how to solve it.
