0

I have a checkbox component, I want my user to be able to check multiple items, and then the items to be saved in the state as an array.

If I select a checkbox my handleChange function seems to set my array to undefined, I'm not sure if it's the way I am sending the data or If I've setup my checkbox wrong, I'm quite new to React.

My main component is

export default class MainForm extends Component {
    state = {
        eventFormats: []
    }

    handleChange = input => event => {
        this.setState({[input]: event.target.value})
        console.log(this.state)
    }

    render() {
        const eventFormat = {eventFormats: this.state.eventFormats}
                return <EventFormat
                    nextStep={this.nextStep}
                    handleChange={this.handleChange}
                    values={eventFormat}
        }
    }
}

My event form component

export default class EventFormat extends Component {
    state = {
        eventFormats: [
            {id: 1, value: 1, label: "Virtual", isChecked: false},
            {id: 2, value: 2, label: "Hybrid", isChecked: false},
            {id: 3, value: 3, label: "Live", isChecked: false},
        ]
    }

    saveAndContinue = (e) => {
        e.preventDefault()
    }

    render() {
        return (
            <Form>
                <h1 className="ui centered">Form</h1>
                <Form.Field>
                    {
                        this.state.eventFormats.map((format) => {
                            return (<CheckBox handleChange={this.props.handleChange} {...format} />)
                        })
                    }
                </Form.Field>
                <Button onClick={this.saveAndContinue}>Next</Button>
            </Form>
        )
    }
}

And finally my checkbox component

const CheckBox = (props) => {
    return (<Checkbox label={props.label} onChange={props.handleChange('eventFormats')}/>)
}

export default CheckBox
1
  • 1
    Yeah, my answer is better as a comment. So you always need to remember, that when you want to console.log the state after setting it you should do it as a callback on setState - this.setState({}, () => {console.log(this.state) }) that is because setState is async. The way you console.log the state in handleChange will not work in a lot of cases Commented Feb 6, 2021 at 18:20

2 Answers 2

1

The error is in your handleChange function, which sets state to a dictionary while you said you want the checkbox's value to be added to the eventFormats array in the state.

export default class MainForm extends Component {
    state = {
        eventFormats: []
    }

    handleChange = input => event => {
        if (event.target.checked) {
            this.setState({eventFormats: this.state.eventFormats.concat([event.target.value])});
        } else {
            const index = this.state.indexOf(event.target.value);
            if (index === -1) {
                console.error("checkbox was unchecked but had not been registered as checked before");
            } else {
                this.setState({eventFormats: this.state.eventFormats.splice(index, 1);
            }
        }
        console.log(this.state)
    }

    render() {
        const eventFormat = {eventFormats: this.state.eventFormats}
                return <EventFormat
                    nextStep={this.nextStep}
                    handleChange={this.handleChange}
                    values={eventFormat}
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

This seems to get into the "checkbox was unchecked" part and then the array is always empty
Should be fixed now @pocockn I used .push which mutates the array but should have used .concat which returns the combined array.
0

There are a few things to fix:

this.setState({[input]: event.target.value})

this will always overwrite the array(eventFormats) with event.target.value.

<CheckBox handleChange={this.props.handleChange} {...format} />

in the above line, you're passing all the properties in each format object

const CheckBox = (props) => {
    return (<Checkbox label={props.label} onChange={props.handleChange('eventFormats')}/>)
}

but here you're only using label and handleChange.


Here's a React StackBlitz that implements what you're looking for. I used <input type="checkbox" />, you can replace this with the Checkbox component you want. See the console logs to know how the state looks after toggling any of the checkboxes.

Also, added some comments to help you understand the changes.

const Checkbox = ({ id, checked, label, handleChange }) => {
  return (
    <>
      <input
        type="checkbox"
        id={id}
        value={checked}
        // passing the id from here to figure out the checkbox to update
        onChange={e => handleChange(e, id)}
      />
      <label htmlFor={id}>{label}</label>
    </>
  );
};

export default class App extends React.Component {
  state = {
    checkboxes: [
      { id: 1, checked: false, label: "a" },
      { id: 2, checked: false, label: "b" },
      { id: 3, checked: false, label: "c" }
    ]
  };

  handleChange = inputsType => (event, inputId) => {
    const checked = event.target.checked;
    // Functional update is recommended as the new state depends on the old state
    this.setState(prevState => {
      return {
        [inputsType]: prevState[inputsType].map(iT => {
          // if the ids match update the 'checked' prop
          return inputId === iT.id ? { ...iT, checked } : iT;
        })
      };
    });
  };

  render() {
    console.log(this.state.checkboxes);
    return (
      <div>
        {this.state.checkboxes.map(cb => (
          <Checkbox
            key={cb.id}
            handleChange={this.handleChange("checkboxes")}
            {...cb}
          />
        ))}
      </div>
    );
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.