6

I want to display the selected checkbox items, for which I'm using material-ui checkbox.

Right now I'm only able to display the items with checkboxes, but I am not able to display the selected items.

I know it is easy but I'm new to reactjs and redux so finding it difficult to start.

Hoping for a help.

Thank you.

this.state = {
            data: [apple, kiwi, banana, lime, orange, grape],
        }}
    handleCheck(x) {
        this.state.checkedValues.push(x);
    }
render(){
       {this.state.data.map((x) =>
             <Checkbox
               label={x} key={x.toString()}
               onCheck={() => this.handleCheck(x)}
               checked=true
              }/>
          )}}
uneet7
  • 2,925
  • 7
  • 24
  • 41
devanya
  • 167
  • 1
  • 1
  • 10
  • I have answered this question [here](https://stackoverflow.com/questions/49861654/rendering-material-ui-next-checkbox-inside-a-redux-form). I hope it helps. :) – Pooja Mahtha Jan 28 '19 at 13:54
  • Kindly accept the answer so that people visiting here get to know the solution quickly – uneet7 Feb 19 '19 at 10:28

6 Answers6

8

Modifying the answer by @BravoZulu by adding the event as the argument in onChange() function.(Also note that use onChange() instead of onCheck() for material-UI checkboxes as shown in the official documentation). Also, don't forget to bind the function in the constructor. I hope this helps the community. Below is the code.

    class App extends Component {
    constructor(props) {
        this.handleCheck = this.handleCheck.bind(this);
        super(props);
        this.state = {
        data: [apple, kiwi, banana, lime, orange, grape],
        checkedValues: []
        }
    }
    handleCheck(e,x) {
        this.setState(state => ({
        checkedValues: state.checkedValues.includes(x)
            ? state.checkedValues.filter(c => c !== x)
            : [...state.checkedValues, x]
        }));
    }

    render() {
        return (<div>
        { this.state.data.map(x =>
            <Checkbox
            label={x} key={x.toString()}
            onChange={e => this.handleCheck(e,x)}
            checked={this.state.checkedValues.includes(x)}
            />
        )}}
        </div>)
    }
}
uneet7
  • 2,925
  • 7
  • 24
  • 41
3

In the handleCheck function, you are attempting to update your component state incorrectly. You need to use setState to make changes to state. In your example, state isn't getting updated so that is probably why you aren't seeing anything get selected. Also, gonna help clean up your example a bit:

class CheckboxList extends React.Component{
  constructor() {
    super();
    this.state = {
        data: ['apple', 'kiwi', 'banana', 'lime', 'orange', 'grape'],
      checkedValues: []
    }
  }
  handleCheck(index) {
    this.setState({
        checkedValues: this.state.checkedValues.concat([index])
    });
    console.log(this.state.checkedValues.concat([index]))
  }
  render(){
   const checks = this.state.data.map( (item, index) => {
         return (
         <span key={item}>
            <input type="checkbox"
           value={item}
           onChange={this.handleCheck.bind(this, index)} //Use .bind to pass params to functions
           checked={this.state.checkedValues.some( selected_index => index === selected_index )}
          />
          <label>{item}</label>
         </span>)
   });
   return <div>{checks}</div>
  }
}

Update: Added working jsfiddle.

Chase DeAnda
  • 15,963
  • 3
  • 30
  • 41
  • 1
    He won't be able to uncheck checkboxes, only check new ones – BravoZulu Jun 15 '17 at 19:23
  • Yeah I kind of gave up on trying to figure out what they were trying to do based on their example. Hopefully this is enough to point them in the right direction. – Chase DeAnda Jun 15 '17 at 19:25
  • Wow, okay I guess I'll actually try to get you something that works. Updated my answer. Let me know if you have questions or want to know why/what is going on. – Chase DeAnda Jun 15 '17 at 19:36
  • `checked={this.state.checkedValues.some( selected_index => index === selected_index )}` what is happening in this line?? – devanya Jun 15 '17 at 19:47
  • I love using the [`.some`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some?v=control) function! It iterates through an array returning either true or false. Once it finds a truthy value, it exits the loop. Basically `.includes` with better performance. In this line, we are looping through the array of selected indexes. We are comparing each selected index to the current item index. If the current item index appears in the `checkedValues` array, we know that it is selected. – Chase DeAnda Jun 15 '17 at 19:50
  • got the explanation... just now I tried you code...I find `this.state.checkedValues` isn't getting updated instantly, ie. in the array `[apple, kiwi, banana, lime, orange, grape]`, after selecting apple and if I check the `checkValues` it is still `[]` – devanya Jun 15 '17 at 19:58
  • You're looking at `this.state.checkedValues` and not `checkValues`? – Chase DeAnda Jun 15 '17 at 20:03
  • Im looking at `this.state.checkedValues` – devanya Jun 15 '17 at 20:05
  • You need to use the `onChange` event not `onCheck`. Here is a working jsfiddle https://jsfiddle.net/jwm6k66c/2919/ – Chase DeAnda Jun 15 '17 at 20:23
  • 1
    Updated jsfiddle to handle unselecting as well: https://jsfiddle.net/jwm6k66c/2920/ – Chase DeAnda Jun 15 '17 at 20:30
3

A bit late to the party but here's a solution using a functional component and hooks

import React, { useState } from 'react';
import Checkbox from '@material-ui/core/Checkbox';

const App = ({ data }) => {
  const [checked, setChecked] = useState([]);

  const handleCheck = (event) => {
    const { value } = event.target;
    setChecked(checked.includes(value) ? checked.filter(c => c !== value) : [...checked, value]);
  };

  return (
    <div>
      {data.map(({ value }) => (
         <Checkbox onChange={e => handleCheck(e)} checked {checked.includes(value)} />
      ))}
   </div>
  );
};

export default App;
2

In React, you shouldn't push data directly to your state. Instead, use the setState function.

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [apple, kiwi, banana, lime, orange, grape],
      checkedValues: []
    }
  }
  handleCheck(x) {
    this.setState(state => ({
      checkedValues: state.checkedValues.includes(x)
        ? state.checkedValues.filter(c => c !== x)
        : [...state.checkedValues, x]
    }));
  }

  render() {
    return (<div>
    { this.state.data.map(x =>
        <Checkbox
          label={x} key={x.toString()}
          onCheck={() => this.handleCheck(x)}
          checked={this.state.checkedValues.includes(x)}
         />
    )}}
    </div>)
  }
}
BravoZulu
  • 1,140
  • 11
  • 24
  • You don't have to copy state each time you do `setState`, you're allowed to just update one piece of state at a time. – Chase DeAnda Jun 15 '17 at 19:39
  • 1
    @ChaseDeAnda It's true, I edited so I just return the updated part of the state – BravoZulu Jun 15 '17 at 19:44
  • 1
    @devanya If you downvote the answer, at least tell me what's wrong with it. – BravoZulu Jun 15 '17 at 19:48
  • But what errors are you getting? I tested this code and it works – BravoZulu Jun 15 '17 at 19:52
  • Passing `props` in to the constructor without using them, using `includes` over `some`, not using `bind` to attach `handleCheck` function, `key={x.toString}` should just be `key={x}` and you already fixed the `setState` issue. Best practices. I like your check to see if the item already exists or not though (not that having dupes in the array would cause issues though). – Chase DeAnda Jun 15 '17 at 19:55
  • 1
    Arrow functions don’t define their own context so ‘this’ is set to the enclosing context, so you don't need to `bind`. `includes` over `some` is insignificant in terms of performance and a matter of preference, not best practices. – BravoZulu Jun 15 '17 at 20:05
  • We use `.bind` to pass in params. By setting the callback to an anonymous function, React will treat that as a change and it will be recreated on each rerender which breaks dom diffing for performance. It may seem small, but little performance wins add up in a big app. – Chase DeAnda Jun 15 '17 at 20:07
0

I was also stuck on this issue for quite some time when I finally found a fix to this. It never works for a functional component which returns a check box. I made a separate class component and wrapped it in Redux Field component and it worked perfectly. I really never understood why it didn't work for the fucntional component as it what is shown in their official example.

I have written the code that worked for me. Hope it helps :)

class CheckBoxInput extends React.Component {

  onCheckBoxSelectChange = (input) => 
  {
    input.onChange();
    this.props.onSelectChange();
  }

  render() {
    const { label, input,} = this.props;
    let name = input.name;

    return (
      <div>
        <InputLabel htmlFor={label} style={{paddingTop: '15px'}}> {label} </InputLabel>
         <FormControl {...input} >
          <Checkbox
            name = {name}
            label = {label}
            color= "primary"
            checked={input.value ? true : false}
            onChange={() => this.onCheckBoxSelectChange(input)}
          />          
        </FormControl>
      </div>

    )
  }
}

const CheckBox = (props) => <Field component={CheckBoxInput} {...props} />;
export default CheckBox;

And to use this checkbox component:

 <CheckBox  name="isCurrent" label="Current" onSelectChange = {this.toggleCurrentEmployerSelection} />
-1

In case you are working with objects instead of simple data types, here is a working approache:

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [{id: 1, name:'banana'},
             {id: 2, name:'kiwi'}],
      checkedValues: []
    }
  }
  handleCheck(element) {
     const values = this.state.checkedValues.filter(e => e.id === element.id).length > 0
            ? this.state.checkedValues.splice( this.state.checkedValues.findIndex( e => e.id === element.id),1)
            : this.state.checkedValues.push(element);
     this.setState({
         checkedValues: values
     });
  }

  render() {
    return (<div>
    { this.state.data.map(el =>
        <Checkbox
           checked={this.state.checkedValues.filter(e => e.id === el.id).length > 0}
           onChange={this.handleCheck.bind(this, el)} //Use .bind to pass params to functions
           value={el}
        />
    )}}
    </div>)
  }
}

So basically what the function handleCheck does is it checks whether the selected object is in the checkedValues array, if that is the case then it deletes it (case uncheck), otherwise it adds it (case check), is i add the checked object to the checkedValues array.

in the Checkbox checked checks whether there is an object in the checkedValues array that is equal to the current loop object, (case checked/unchecked)

Sadik anass
  • 282
  • 4
  • 9