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.
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/>