pocockn
pocockn

Reputation: 2063

Collect checkbox values as an array React

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

Upvotes: 0

Views: 1309

Answers (2)

Jonas
Jonas

Reputation: 1573

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

Upvotes: 1

Ramesh Reddy
Ramesh Reddy

Reputation: 10652

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

Upvotes: 0

Related Questions