KathA
KathA

Reputation: 33

Updating state with multiple radio button groups in one component react

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

Answers (1)

Amila Senadheera
Amila Senadheera

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});

1st way

The above line should be replaced with the following.

setQuestionState({ ...questionState, options: nextState });

Edit modest-ramanujan-mj25i

2nd way

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 };
          }
        })
      };
    });
  };

Edit cranky-cohen-cjseb

Upvotes: 1

Related Questions