10

I am trying to build a filter for my portfolio website. Checkboxes that let you pick a technology (react, redux, jquery etc.) to display a piece of work(s) that contain(s) that/those technologies. So every time the user clicks on a box, I want to add the value (JavaScript, Redux, React etc.) to an array that I use in another function to check against my portfolio pieces and filter out what isn't there.

I am finding this very difficult and I think it should be quite simple. Can someone point me in the right direction? Is there a way to simply have a function trigger (onChange callback?) that reads the checked/unchecked status of my form input elements and then updates my state array accordingly? Can I get the status of all the checkboxes simply in React? Do I need to have individual state of checked/unchecked for my checkboxes?

It seems that jQuery makes it pretty possible with selectors with:

$('input[type="checkbox"]:checked').each(function () {}
2
  • Do you need them in the order they appear as checkboxes? Or do you just want them to be appended to the end of the list in the order they are checked? Commented May 10, 2016 at 5:59
  • This is how I do it: onSubmit(event) { const data = Array.from(event.currentTarget.elements).filter(el => (el.checked && el.getAttribute('type') === 'checkbox')).map(el => el.value); } Commented Jun 14, 2017 at 16:56

2 Answers 2

23

If you don't care about the order and you just want to append the items to the array as they appear we could definitely do exactly what you suggest in your question. On the change event of the checkbox check if the box is checked or or unchecked (event.target.checked returns true if checked or false if unchecked) and handle the array logic accordingly. this is a simple representation of how that could work:

import React, { Component } from 'react'
import { connect } from 'react-redux'

class Portfolio extends Component {
  constructor() {
    super()
    // initialize your options array on your state
    this.state = {
      options: []
    }
  }

  onChange(e) {
    // current array of options
    const options = this.state.options
    let index

    // check if the check box is checked or unchecked
    if (e.target.checked) {
      // add the numerical value of the checkbox to options array
      options.push(+e.target.value)
    } else {
      // or remove the value from the unchecked checkbox from the array
      index = options.indexOf(+e.target.value)
      options.splice(index, 1)
    }

    // update the state with the new array of options
    this.setState({ options: options })
  }

  render() {
    return (
      <main className='portfolio'>

        <form>
          <div className="input-group">
            <label>cb1</label>
            <input type="checkbox" value={1} onChange={this.onChange.bind(this)} />
          </div>
          <div className="input-group">
            <label>cb2</label>
            <input type="checkbox" value={2} onChange={this.onChange.bind(this)} />
          </div>
          <div className="input-group">
            <label>cb3</label>
            <input type="checkbox" value={3} onChange={this.onChange.bind(this)} />
          </div>
        </form>

        <div className="selected-items">
          {this.state.options.map(number => 
             <p key={number}>item: {number}</p>
          )}
        </div>

      </main>
    )
  }
}

if you DO care about order, if you can append numerical values to the array like I did in this example you could easily give your checkboxes sorted numerical values and you could sort the array before updating your state so it's always in a certain order regardless of the order they are checked.

  onChange(e) {
    // current array of options
    const options = this.state.options
    let index

    // check if the check box is checked or unchecked
    if (e.target.checked) {
      // add the numerical value of the checkbox to options array
      options.push(+e.target.value)
    } else {
      // or remove the value from the unchecked checkbox from the array
      index = options.indexOf(+e.target.value)
      options.splice(index, 1)
    }

    // sort the array
    options.sort()    

    // update the state with the new array of options
    this.setState({ options: options })
  }
Sign up to request clarification or add additional context in comments.

2 Comments

What about keeping the checked state of each checkbox?
I know this is old, but there are a few new questions that have linked here, so I want to point out that onChange mutates state, and should be changed to const options = [...this.state.options]
2

Here's how I'm doing it:

// util.js
import getPath from 'lodash/get';
import setIn from 'lodash/fp/set';

export function linkMultiCheck(name, value) {
    return {
        checked: getPath(this.state, name, []).includes(value),
        onChange: ev => {
            let values = getPath(this.state, name, []);
            if(ev.target.checked) {
                values = [...values, value];
            } else {
                values = values.filter(v => v !== value);
            }
            this.setState(setIn(name, values));
        },
    }
}

// form.js
<ul>
    {options.branches.map(branch => (
        <li key={branch.id} className="checkbox">
            <label>
                <input type="checkbox" name={this.id} {...this::linkMultiCheck('formData.branchIds',branch.id)}/>
                {branch.id}
            </label>
        </li>
    ))}
</ul>

i.e., if a checkbox is checked, append it to the current array of values. If it's unchecked, filter it out.

I'm using lodash here so that we can set deeply nested state values using dot notation.

1 Comment

this works fine even without lodash ( in my case ) . Thanks dude!

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.