Home > database >  Getting error "Uncaught Error: Objects are not valid as a React child", but I'm not p
Getting error "Uncaught Error: Objects are not valid as a React child", but I'm not p

Time:11-03

I'm attempting to map an array of objects, obtained from an axios.get request, as a set of React component children. Here is the full error I'm receiving:

Uncaught Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

My goal here is to create an Admin component where new shows can be created, and existing shows can be edited and deleted. I've confirmed that the axios request's response.data is an array of objects, and I'm not passing the objects themselves as children. I'm passing them as props to <ShowListing /> components, and I know that this component works with those props. Can someone please help me figure out what's going wrong here?

Here is the code for the Admin component. It seems that the error is occurring from lines 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

Here is the code for the ShowListing component:

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

UPDATE: Per phasma's suggestion, I removed async from the Admin component and the useEffect. This cleared the original error, but now I'm receiving a new error:

Admin.js:107 Uncaught TypeError: Cannot read properties of null (reading 'map')

Why is currShows null when it's being set in the useEffect?

CodePudding user response:

You're marking your Admin component async, which is not valid for a React component. Remove that modifier and the error should clear up.

edit; To answer your new question, currShows is null until the call is completed, which is likely after the first render. You should write something like

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

to handle that case explicitly. The null could be a loading spinner or something to indicate something's working in the background, but hopefully that helps clear your error.

  • Related