Unhandled Rejection (TypeError): Cannot read properties of undefined (reading 'reduce')

Viewed 1632

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:

enter image description here

But I don't know how to solve it.

1 Answers

replace

.required("A random message")
.nullable(false)

to

.test("fileLength", "A random message", (value) => {
        return !!value.length
})
Related