Search code examples
reactjsjsx

MERN add DB items in dropdown menu. Error .map is not a function


I am having some trouble with trying to list the MongoDB items in my REACT application. The aim is to add an inventory item, but have it set to a specific group. I am fetching from the DB and rendering it as an option in the dropdown. Everytime I run the app and change the option to one of these DB values, it throws the error

TypeError: group.map is not a function

Here is the code for AddInventoryForm.jsx

import { useState, useEffect } from "react"
import { useNavigate } from 'react-router-dom'

const InventoryForm = () => {
    const initialState = {
        _id: '0',
        group_name: 'select_group',
        notes: 'null'
      }
    const [title, setTitle] = useState('')
    const [description, setDescription] = useState('')
    const [group, setGroup] = useState([initialState])
    const [status, setStatus] = useState('')
    const [error, setError] = useState(null)
    const navigate = useNavigate()

    
    useEffect(() => {
        // Fetch groups for dropdown list
        const fetchGroups = async () => {
            try {
                const response = await fetch('/api/groups')

                if (!response.ok) {
                    throw new Error('Failed to fetch group')
                }
                
                const contentType = response.headers.get('content-type')
                if (!contentType || !contentType.includes('application/json')) {
                    throw new Error('Invalid response format - expected JSON')
                }
                
                const json = await response.json()
                console.log("Groups = ", json)
                setGroup(json)
            } catch (error) {
                console.error('Error fetching group:', error)
            }
        }
        fetchGroups()
    }, [])

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

        const inventory = {title, description, group, status}

        // Manual check for if group dropdown has been changed
        if(group==='select_group') {
            return setError("Group is required")
        }

        const response = await fetch('/api/inventory', {
            method: 'POST',
            body: JSON.stringify(inventory),
            headers: {
                'Content-Type': 'application/json'
            }
        })
        console.log(response)
        const json = await response.json()

        

        if(!response.ok) {
            setError(json.error)
        }
        if(response.ok) {
            //setTitle("")
            //setDescription("")
            //setGroup("")
            //setStatus("")
            setError(null)
            console.log("New inventory item added", json)

            // Alert user
            alert("Item added")
            
            // Navigate back to inventory
            navigate("/inventory/")
        }


    }
    return (
        <form className="create-inventory-form" onSubmit={handSubmit}>
            {/*<h3>Add Inventory Item</h3>*/}

            <label>Title</label> <br/>
            <input
                type="text"
                onChange={(e) => setTitle(e.target.value)}
                value={title}
            /> <br/>
            <label>Description</label> <br/>
            <input
                type="text"
                onChange={(e) => setDescription(e.target.value)}
                value={description} 
            /> <br/>
            <label>Group</label> <br/>

            <select
            value={initialState}
            onChange={(e) => setGroup(e.target.value)}>
                <option value={initialState}>Select Group</option>
                <option value={group.group_name}>{group.group_name}</option>

                {group && group.map((groupItem) => (
                <option key={groupItem._id} value={groupItem._id}>{groupItem.group_name}</option>))}

            </select><br/>
            <label>Status</label> <br/>
            <input
                type="text"
                onChange={(e) => setStatus(e.target.value)}
                value={status} 
            /> <br/>

            <button>Submit</button>
            {error && <div className="error-msg">{error}</div>}
        </form>
    )
}

export default InventoryForm

I tried changing the useState hook to various different things, but no luck sadly.


Solution

  • I found how to solve the issue.

    I first added the state selectedGroupId which is used for handling the state change for the dropdown. I also made the initial state an array so it doesnt break things when the database pulls and sets the state.

    const initialState = {
        _id: '0',
        group_name: 'select_group',
        notes: 'null'
    }
    const [title, setTitle] = useState('')
    const [description, setDescription] = useState('')
    const [groups, setGroups] = useState([])
    const [selectedGroupId, setSelectedGroupId] = useState(initialState._id)
    const [status, setStatus] = useState('select_status')
    const [error, setError] = useState(null)
    const navigate = useNavigate()
    

    And then reference it in the render.

    <label>Group</label> <br/>
    <select
        value={selectedGroupId}
        onChange={(e) => setSelectedGroupId(e.target.value)}>
        <option value={initialState._id}>Select Group</option>
        {groups.map((groupItem) => (
            <option key={groupItem._id} value={groupItem._id}>{groupItem.group_name}</option>
        ))}
    </select><br/>