Search code examples
reactjstypescriptonchange

How to handle checkbox change dynamically using react onchange


Hello how can I handle onChange for multiple check box? The following example allows me to control the value of one checkbox.

class App extends Component {
  constructor() {
    super();
    this.state = {
      i_agree: false
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
     
  handleChange(event) {
    this.setState({i_agree: !this.state.i_agree});
  }
     
  handleSubmit(event) {
    console.log(this.state);
    event.preventDefault();
  }
     
  render() {
    return (
      <div>
        <h1>React Checkbox onChange Example - ItSolutionStuff.com</h1>
        <form onSubmit={this.handleSubmit}>
           
          <label>
            <input
              type="checkbox"
              defaultChecked={this.state.i_agree}
              onChange={this.handleChange}
            /> I Agree with this content...
          </label>
         <label>
            <input
              type="checkbox"
              defaultChecked={this.state.isChecked}
              onChange={this.handleChange}
            /> I want to control this...
          </label>
           
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}

How can I set my handleChange function to handle changes for each checkbox instead of checking for one.

Reference Link

Attempt 1

class App extends Component {
      constructor() {
        super();
        this.state = {
          i_agree: false,
          isChecked: false

        };
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      }
         
      handleChange(event) {
        this.setState({
          [e.target.name]: !e.target.value
        });
      }
         
      handleSubmit(event) {
        console.log(this.state);
        event.preventDefault();
      }
         
      render() {
        return (
          <div>
            <h1>React Checkbox onChange Example - ItSolutionStuff.com</h1>
            <form onSubmit={this.handleSubmit}>
               
              <label>
                <input
                  type="checkbox"
                  defaultChecked={this.state.i_agree}
                  onChange={this.handleChange}
                  name="i_agree"
                /> I Agree with this content...
              </label>
             <label>
                <input
                  type="checkbox"
                  defaultChecked={this.state.isChecked}
                  onChange={this.handleChange}
                  name="isChecked"
                /> I want to control this...
              </label>
               
              <input type="submit" value="Submit" />
            </form>
          </div>
        );
      }
    }

I Updated the onChange handler and added name attributes in each label. But this did not work or threw any errors.


Solution

  • I would use checked instead of defaultChecked to have a controlled checkbox input.

    for handleChange you can use name and checked attributes to update checkbox state (you could also do a flip on its boolean state instead of using checked). this way you can have only one handler for all your checkboxes.

    Typescript solution

    import React from "react";
    
    interface CheckboxProps {
      checked: boolean;
      label: string;
      name: string;
      onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
    }
    // I abstracted Checkbox to a component but it's up to you
    const Checkbox = ({ checked, onChange, label, name }: CheckboxProps) => (
      <label>
        <input
          type="checkbox"
          checked={checked} // use checked to have a controlled component
          onChange={onChange}
          name={name}
        />
        {label}
      </label>
    );
    
    interface AppState {
      iAgree: boolean;
      wantCookies: boolean;
      wishList: boolean;
    }
    
    class App extends React.Component<{}, AppState> {
      // constructor method is called implicit you can define state outside
      // for binding methods, you can declare them as arrow functions
      state = {
        iAgree: false,
        wantCookies: false,
        wishList: false
      };
    
      // extract name and checked properties
      handleChange = ({
        target: { name, checked }
      }: React.ChangeEvent<HTMLInputElement>) =>
        this.setState({ [name]: checked } as Pick<AppState, keyof AppState>);
    
      handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
        console.log(this.state);
        event.preventDefault();
      };
    
      render() {
        const { iAgree, wantCookies, wishList } = this.state;
        return (
          <div>
            <h1>React Checkbox onChange Example - ItSolutionStuff.com</h1>
            <form onSubmit={this.handleSubmit}>
              <Checkbox
                onChange={this.handleChange}
                name={"iAgree"}
                checked={iAgree}
                label={"I agree with terms"}
              />
              <Checkbox
                onChange={this.handleChange}
                name={"wantCookies"}
                checked={wantCookies}
                label={"I want more cookies"}
              />
              <Checkbox
                onChange={this.handleChange}
                name={"wishList"}
                checked={wishList}
                label={"Put Me on wishlist"}
              />
              <br />
              <input type="submit" value="Submit" />
            </form>
          </div>
        );
      }
    }
    
    export default App;
    

    Javascript solution

    import React from "react";
    
    // I abstracted Checkbox to a component but it's up to you
    const Checkbox = ({ checked, onChange, label, name }) => (
      <label>
        <input
          type="checkbox"
          checked={checked} // use checked to have a controlled component
          onChange={onChange}
          name={name}
        /> { label }
      </label>
    )
    
    class App extends React.Component {
        // constructor method is called implicit you can define state outside
        // for binding methods, you can declare them as arrow functions
        state = {
          iAgree: false,
          wantCookies: false,
          wishList: false,
        };
    
      // extract name and checked properties
      handleChange = ({target: { name, checked }}) => this.setState({[name]: checked});
      
      handleSubmit= (event) => {
        console.log(this.state);
        event.preventDefault();
      }
         
      render() {
        const { iAgree, wantCookies, wishList } = this.state
        return (
          <div>
            <h1>React Checkbox onChange Example - ItSolutionStuff.com</h1>
            <form onSubmit={this.handleSubmit}>
              <Checkbox onChange={this.handleChange} name={'iAgree'} checked={iAgree} label={'I agree with terms'} />
              <Checkbox onChange={this.handleChange} name={'wantCookies'} checked={wantCookies} label={'I want more cookies'} />
              <Checkbox onChange={this.handleChange} name={'wishList'} checked={wishList} label={'Put Me on wishlist'} />
              <br/>
              <input type="submit" value="Submit" />
            </form>
          </div>
        );
      }
    }
    
    export default  App
    

    fwiw I removed the constructor since it's not necessary. You can keep it though, it's only a cleaner way to declare your classes.

    sandbox with implementation TS: https://codesandbox.io/s/so-multi-checkboxes-ts-qz20m

    sandbox with implementation JS: https://stackblitz.com/edit/so-multi-checkboxes?file=src/App.js