reactjs 获取错误“未捕获的错误:对象作为React子级”“无效,但我不会将对象作为子级传递

rnmwe5a2  于 2022-11-04  发布在  React
关注(0)|答案(1)|浏览(84)

我试图将从axios.get请求中获得的对象数组Map为一组React组件子级。下面是我收到的完整错误:
<ShowListing />
我在这里的目标是创建一个Admin组件,在这个组件中可以创建新的节目,可以编辑和删除现有的节目。我已经确认axios请求的response.data是一个对象数组,我没有将对象本身作为子对象传递,而是将它们作为道具传递给<ShowListing />组件。我知道这个组件可以和那些道具一起工作。有人能帮我找出哪里出了问题吗?
下面是Admin组件的代码。错误似乎发生在第107-113行:

import { useState, useEffect } from 'react'
import styles from './Admin.module.css'
import axios from 'axios'
import FormData from 'form-data'
import ShowListing from './ShowListing.js'

const Admin = async () => {
    const [formValues, setFormValues] = useState({
        eventTitle: null,
        location: null,
        date: null,
        time: null,
        ticket: null,
        desc: null,
        image: null
    })

    const [currShows, setCurrShows] = useState(null)

    useEffect(async () => {
        axios.get("http://localhost:4000/getShows").then(response => {
            console.log(response.data)
            setCurrShows(response.data)
        })
    })

    const handleSubmit = async (e) => {
        e.preventDefault()

        const formData = new FormData()

        // FILE READER
        const getImageFile = () => {
            return new Promise(resolve => {
                const reader = new FileReader()
                reader.onload = function () {
                    resolve(reader.result)
                    // console.log(`IMAGE FILE:\n ${imageFile}`) // imageFile IS NOT UNDEFINED HERE, BASE64 STRING
                }
                reader.readAsDataURL(document.getElementById("image").files[0])
            })
        }

        const imageFile = await getImageFile()

        Array.from(document.getElementById("form").elements).forEach(element => {           
            switch (element.name){
                case "image":
                    formData.append(`${element.name}`, imageFile) // UNDEFINED. WHY?
                    break
                case "submit":
                    break
                default:
                    formData.append(`${element.name}`, element.value)
            }
        })

        console.log([...formData])

        try {
            const response = axios.post('http://localhost:4000/uploadShow', formData)
            console.log(response)
            alert('NEW SHOW SUBMITTED')
            document.getElementById("form").reset()

        } catch (e) {
            alert(e)
            console.log(e)
        }
    }

    return (
        <div>
            <div className={styles.main}>
                <div className={styles.titleContainer}>
                    <h1>KMAC ADMIN</h1>
                </div>                
                <div className={styles.formWindow}>
                    <div className={styles.newShowHeader}>
                        <h1>New Show</h1>
                        <form className={styles.showForm} id="form" method="post" encType="multipart/form-data" onSubmit={e => handleSubmit(e)}>
                            <label htmlFor="eventTitle">Event Title: </label>
                            <input className={styles.fieldInput} type="text" name="eventTitle" onChange={e => setFormValues({...formValues, eventTitle: e.target.value})}/>
                            <br />
                            <label htmlFor="location">Location: </label>
                            <input className={styles.fieldInput} type="text" name="location"onChange={e => setFormValues({...formValues, location: e.target.value})} />
                            <br />
                            <label htmlFor="date">Date: </label>
                            <input className={styles.fieldInput} type="date" name="date" onChange={e => setFormValues({...formValues, date: e.target.value})}/>
                            <br />
                            <label htmlFor="time">Time: </label>
                            <input className={styles.fieldInput} type="time" name="time" onChange={e => setFormValues({...formValues, time: e.target.value})}/>
                            <br />
                            <label htmlFor="ticket">Ticket: </label>
                            <input className={styles.fieldInput} type="text" name="ticket" onChange={e => setFormValues({...formValues, ticket: e.target.value})}/>
                            <br />
                            <textarea name="desc" placeholder="Event Description" rows="8" onChange={e => setFormValues({...formValues, desc: e.target.value})}/>
                            <br />
                            <label htmlFor="image">Select Image &#40;15MB or less&#41;: </label>
                            <input type="file" id="image" name="image" accept="image/jpeg" onChange={e => setFormValues({...formValues, image: e.target.files})}/>
                            <br />
                            <button className={styles.submit} name="submit" type="submit">Submit</button>
                        </form>
                    </div>
                </div>
            </div>
            <div>
                {
                    currShows.map(show => {
                        return <ShowListing params={show} />
                    })
                }
            </div>
        </div>
    )
}

export default Admin

以下是ShowListing组件的代码:

import { useState } from 'react'
import axios from 'axios'
import styles from './ShowListing.module.css'

// SHOW OBJECT SHAPE:
//     eventTitle: null,
//     location: null,
//     date: null,
//     time: null,
//     ticket: null,
//     desc: null,
//     image: null

const ShowListing = (props) => {
    // Toggle deletion warning
    const [deleteWarning, setDeleteWarning] = useState(false)
    const [editForm, setEditForm] = useState(false)
    const [formValues, setFormValues] = useState({
        eventTitle: null,
        location: null,
        date: null,
        time: null,
        ticket: null,
        desc: null,
        image: null
    })

    // covert props.params.date to format "year-month-day", call for date input default value
    const dateConvert = () => {
        const dateArr = props.params.date.split('-')
        const year = dateArr.pop()
        dateArr.unshift(year)
        return dateArr.join('-')
    }

    // covert props.param.time to 24-hour format, call for time input default value
    const timeConvert = () => {
        const timeArr = props.params.time.split(' ')
        const time = timeArr[0].split(":")
        if (timeArr[1] === 'PM')
            time[0] = ((parseInt(time[0])) + 12).toString()
        if (parseInt(time[0]) < 10)
            time[0] = "0" + time[0]
        return time.join(":")
    }

    const handleDelete = () => {
        // TODO: delete request with props.params._id

        // Alert deletion and reload page
        alert(`SHOW DELETED:\n${props.params.eventTitle}`)
        window.location.reload()
    }

    const handleEditSubmit = (e) => {     
        e.preventDefault()

        // TODO: post request for show update with props.params._id

        console.log(formValues)
        alert(`SHOW EDITED:\n${formValues.eventTitle}\nFORMERLY:\n${props.params.eventTitle}`)
        window.location.reload()
    }

    return (
        <div className={styles.temp}>
        <div className={styles.container}>
            {
                deleteWarning &&
                <div className={styles.deleteWarning}>
                    <div><p>Delete this show listing?</p></div>
                    <div><button className={`${styles.deleteButton} ${styles.deleteYes}`} onClick={handleDelete}>Yes</button></div>
                    <div><button className={`${styles.deleteButton} ${styles.deleteNo}`} onClick={() => setDeleteWarning(false)}>No</button></div>
                </div>
            }
            <div className={styles.title}>
                <p>{props.params.eventTitle}</p>
            </div>
            <div className={styles.date}>
                <p>{`${props.params.date} -- ${props.params.time}`}</p>
            </div>
            <div className={styles.edit} onClick={() => setEditForm(true)}>
                <img src="images/icons8-edit-30.png" />
            </div>
            <div className={styles.delete} onClick={() => setDeleteWarning(true)}>
                <img src="images/icons8-trash-30.png" />
            </div>
            <br/>
        </div>
        {
            editForm &&
            <div className={styles.formContainer}>
                <div className={styles.formFrame}>
                    <form id="editForm" onSubmit={e => handleEditSubmit(e)}>
                        <label className={styles.formLabel} htmlFor="eventTitle">Event Title: </label>
                        <br />
                        <input className={styles.fieldInput} type="text" name="eventTitle" defaultValue={props.params.eventTitle} onChange={e => setFormValues({...formValues, eventTitle: e.target.value})}/>
                        <br />
                        <label className={styles.formLabel} htmlFor="location">Location: </label>
                        <br />
                        <input className={styles.fieldInput} type="text" name="location" defaultValue={props.params.location} onChange={e => setFormValues({...formValues, location: e.target.value})} />
                        <br />
                        <label className={styles.formLabel} htmlFor="date">Date: </label>
                        <br />
                        <input className={styles.fieldInput} type="date" name="date" defaultValue={dateConvert()} onChange={e => setFormValues({...formValues, date: e.target.value})}/>
                        <br />
                        <label className={styles.formLabel} htmlFor="time">Time: </label>
                        <br />
                        <input className={styles.fieldInput} type="time" name="time" defaultValue={timeConvert()} onChange={e => setFormValues({...formValues, time: e.target.value})}/>
                        <br />
                        <label className={styles.formLabel} htmlFor="ticket">Ticket: </label>
                        <br />
                        <input className={styles.fieldInput} type="text" name="ticket" defaultValue={props.params.ticket} onChange={e => setFormValues({...formValues, ticket: e.target.value})}/>
                        <br />
                        <br />
                        <textarea className={styles.formDesc} name="desc" placeholder="Event Description" rows="8" defaultValue={props.params.desc} onChange={e => setFormValues({...formValues, desc: e.target.value})}/>
                        <br />
                        <label className={styles.formLabel} htmlFor="image">Please update image &#40;15MB or less&#41;: </label>
                        <input style={{color: "red"}} type="file" id="image" name="image" accept="image/jpeg" onChange={e => setFormValues({...formValues, image: e.target.files})}/>
                        <br />
                        <br />
                        <button className={styles.submit} name="submit" type="submit">Submit</button>
                        <button name="cancel" onClick={() => setEditForm(false)}>Cancel</button>
                    </form>
                </div>
            </div>
        }
        </div>        
    )
}

export default ShowListing

更新:根据phasma的建议,我从Admin组件和useEffect中删除了async。这清除了原来的错误,但现在我收到了一个新的错误:
Admin.js:107 Uncaught TypeError: Cannot read properties of null (reading 'map')
为什么在useEffect中设置currShows时它为空?

8ehkhllq

8ehkhllq1#

您将Admin组件标记为async,这对于React组件是无效的。删除该修饰符,错误就会清除。
edit;为了回答您的新问题,currShows在调用完成之前为null,这很可能是在第一次呈现之后。

currShows === null ? null : currShows.map(show => { /* ... */

null可能是一个加载微调器或者其他指示后台正在工作的东西,但希望这能帮助清除错误。

相关问题