Reputation: 33
I am trying to update my component's state with the radio buttons' values. When I make the second radio button selection (reason for status) I get an error telling me
Uncaught TypeError: cannot read properties of undefined (reading 'map')
When I click the submit button, the state is only showing the state for the first group of radio buttons ie current status and then it is showing the selected value as an empty string (default state value) and not the value as per the radio button selection
Can anyone help? Here is my code:
import {Container, Row, Col, Form, Button } from 'react-bootstrap';
import { useState } from 'react';
const Question = (q) => {
//state to hold responses from input
const [questionState, setQuestionState] = useState({
options: [
{ radioName: 'currentstatus',
selected: '' },
{ radioName: 'reasonstatus',
selected: ''},
{ radioName: 'actions',
selected: ''},
{ radioName: 'actowner',
selected: ''},
{ radioName: 'actstat',
selected: ''}
]
});
//EVENT HANDLERS
//radio buttons onchange
const handleChange = (e)=>{
const { options } = questionState;
const {name, value} = e.target;
console.log(name, value);
const nextState = options.map( opt => {
if (opt.radioName !== name) {
return {...opt}
} else {
return {...opt,
selected: value}
}
});
setQuestionState(...options, {nextState});
}
//date picker event
const handleDate = (e) => {
console.log(e.target.value);
}
//overall form submission
const handleSubmit = (e) => {
console.log(questionState);
}
const question = q;
return(
<Container>
<div className="question mb-5">
<Form className="border border-3 border-primary p-3 m-2">
<Form.Group className="mb-2">
<Row className="bg-primary text-white p-2">
<div className="fs-5">{question.question}</div>
</Row>
{/* ---------------- CURRENT STATUS ----------------------- */}
<Form.Label className="fs-4 fw-bold">Current Status:</Form.Label>
<div key="currStat" className="mb-3">
<Form.Check inline label="Fully meeting expectation" name="currentstatus" type="radio" id='status-1' value="1" onChange={handleChange}/>
<Form.Check inline label="Partially meeting expectation" name="currentstatus" type="radio" id='status-2' value="2" onChange={handleChange}/>
<Form.Check inline label="Not meeting expectation" name="currentstatus" type="radio" id='status-3' value="3" onChange={handleChange}/>
<Form.Check inline label="Not applicable" name="currentstatus" type="radio" id='status-4' value="4" onChange={handleChange}/>
</div>
{/* -------------------- REASONS FOR STATUS --------------------------- */}
<Form.Label className="fs-4 fw-bold">Reason for Status:</Form.Label>
<div key="reasStat" className="mb-3">
<Form.Check inline label="Reason 1" name="reasonstatus" type="radio" id='reason-1' value="1" onChange={handleChange}/>
<Form.Check inline label="Reason 2" name="reasonstatus" type="radio" id='reason-2' value="2" onChange={handleChange}/>
<Form.Check inline label="Reason 3" name="reasonstatus" type="radio" id='reason-3' value="3" onChange={handleChange}/>
<Form.Check inline label="Reason 4" name="reasonstatus" type="radio" id='reason-4' value="4" onChange={handleChange}/>
</div>
{/* -------------------- ACTIONS --------------------------- */}
<Form.Label className="fs-4 fw-bold">Action to be taken:</Form.Label>
<div key="actions" className="mb-3">
<Form.Check inline label="Action 1" name="actions" type="radio" id='act-1' value="1" onChange={handleChange}/>
<Form.Check inline label="Action 2" name="actions" type="radio" id='act-2' value="2" onChange={handleChange}/>
<Form.Check inline label="Action 3" name="actions" type="radio" id='act-3' value="3" onChange={handleChange}/>
<Form.Check inline label="Action 4" name="actions" type="radio" id='act-4' value="4" onChange={handleChange}/>
</div>
{/* -------------------- ACTION OWNER --------------------------- */}
<Form.Label className="fs-4 fw-bold">Action Owner:</Form.Label>
<div key="actOwner" className="mb-3">
<Form.Check inline label="User 1" name="actowner" type="radio" id='user-1' value="1" onChange={handleChange}/>
<Form.Check inline label="User 2" name="actowner" type="radio" id='user-2' value="2" onChange={handleChange}/>
</div>
{/* -------------------- ACTION STATUS --------------------------- */}
<Form.Label className="fs-4 fw-bold">Action Status:</Form.Label>
<div key="actStat" className="mb-3">
<Form.Check inline label="Not started" name="actstat" type="radio"id='actStat-1' value="1" onChange={handleChange}/>
<Form.Check inline label="In progress" name="actstat" type="radio" id='actStat-2' value="2" onChange={handleChange}/>
<Form.Check inline label="Completed" name="actstat" type="radio" id='actStat-3' value="3" onChange={handleChange}/>
</div>
{/* --------------------- DATE PICKER ------------------------- */}
<Row>
<Col xs={4}>
<Form.Label className="fs-4 fw-bold">Due Date:</Form.Label>
<Form.Control xs={4} type="date" className="mb-3" onChange={handleDate}/>
</Col>
</Row>
<Row className="justify-content-end">
<Col xs={2}>
<Button onClick = {handleSubmit}>Submit</Button>
</Col>
</Row>
</Form.Group>
</Form>
</div>
</Container>
)
}
export default Question;
Upvotes: 0
Views: 1787
Reputation: 13245
In handleChange
, the following is not a valid state update. Due to that, your state was missing options
entry after each radio button click.
setQuestionState(...options, {nextState});
The above line should be replaced with the following.
setQuestionState({ ...questionState, options: nextState });
It is always safe to use the callback version of updating the state. Therefore, handleChange
function should be corrected as below.
const handleChange = (e) => {
const { name, value } = e.target;
setQuestionState((prevState) => {
return {
...prevState,
options: prevState.options.map((opt) => {
if (opt.radioName !== name) {
return { ...opt };
} else {
return { ...opt, selected: value };
}
})
};
});
};
Upvotes: 1