2

I am new to React and I am trying to make a table of users and want to use checkboxes to manage their permissions. I have run into a problem with updating the state when I click a checkbox. The handleChange method is where I am having problems. I don't know how to go about identifying the right checkbox to change the state for that specific user. I am thinking maybe I need to add an id prop to each but that seems like it might get out of hand for a large number of users, i.e. one id for each permission per user. I feel like this shouldn't be so difficult but I have been stuck for a long time.

My component code is below.

import React from 'react'
import {Link} from 'react-router'
import {Panel, Button, PageHeader, Row, Col, Table, Input} from 'react-bootstrap'


export class UserPermissions extends React.Component {

constructor() {
    super();
    this.state = {
        users: [
            {   
                name: 'Jerry',
                viewAccounts: true,
                modifyAccounts: true,
                viewUsers: false,
                modifyUsers: true
            },
            {   
                name: 'George',
                viewAccounts: false,
                modifyAccounts: true,
                viewUsers: false,
                modifyUsers: false
            },
            {   
                name: 'Elaine',
                viewAccounts: true,
                modifyAccounts: false,
                viewUsers: false,
                modifyUsers: true
            }
    ]
    }               
}   

handleChange(e){
  //not sure how to write this
}

renderHeadings(data){
    return data.map((step, index) => <th key={index} style={{align:"center"}}>{step}</th>);

}

renderRows(data){
    return data.map((step, index) => 
            <tr key={index}>
                <td>{step['name']}</td>
                <td style={{align:"center", paddingLeft:"40px"}}>
                    <Input type="checkbox"
                           checked={step['viewAccounts']} 
                           onChange={this.handleChange}/></td>
                <td style={{align:"center", paddingLeft:"40px"}}>
                    <Input type="checkbox"
                           checked={step['modifyAccounts']} 
                           onChange={this.handleChange}/></td>
                <td style={{align:"center", paddingLeft:"40px"}}>
                    <Input type="checkbox"
                           checked={step['viewUsers']} 
                           onChange={this.handleChange}/></td>
                <td style={{align:"center", paddingLeft:"40px"}}>
                    <Input type="checkbox"
                           checked={step['modifyUsers']}
                           onChange={this.handleChange} /></td>
                <td style={{align:"center"}}>
                    <Link to="/users"><i className="fa fa-edit fa-2x fa-fw" /></Link>
                    <Link to="/users"><i className="fa fa-times-circle fa-2x fa-fw" /></Link></td>
            </tr>
    );

}

render() {
    return (
        <div>
             <Row>
                <Col lg={12}>
                    <PageHeader>User Permissions</PageHeader>
                </Col>

                <Col lg={12}>
                    <Panel header={<span>Users</span>} bsStyle="primary">
                        <div>
                            <div className="dataTable_wrapper">
                                <div id="dataTables-example_wrapper" className="dataTables_wrapper form-inline dt-bootstrap no-footer">
                                    <Row>
                                        <Col sm={12}>
                                            <Table striped condensed responsive>
                                                <thead>
                                                <tr>
                                                    {this.renderHeadings(this.props.headings)}
                                                </tr>
                                                </thead>
                                                <tbody>
                                                    {this.renderRows(this.state.users)}
                                                </tbody>
                                            </Table>
                                        </Col>
                                    </Row>
                                </div>
                            </div>
                        </div>
                    </Panel>
                    <Button bsStyle="success">Add User</Button>
                </Col>
            </Row>
        </div>
        );
}
}

UserPermissions.propTypes = {
headings: React.PropTypes.array
}

UserPermissions.defaultProps = {
headings: ['Name', 'View Accounts', 'Modify Accounts', 'View Users', 'Modify Users']

}
1
  • Call handle change with step value like: this.handleChange('modifyUsers') or you can use refs to identify the checkbox. Commented Aug 9, 2016 at 15:09

2 Answers 2

3

First, you should add id's to each user. Identifying users by their name is a bad practice:

constructor() {
  super();
  this.state = {
    users: [
      {   
        id: 1,
        name: 'Jerry',
        viewAccounts: true,
        modifyAccounts: true,
        viewUsers: false,
        modifyUsers: true
      },
      { 
        id: 2,  
        name: 'George',
        viewAccounts: false,
        modifyAccounts: true,
        viewUsers: false,
        modifyUsers: false
      },
      {   
        id: 2,
        name: 'Elaine',
        viewAccounts: true,
        modifyAccounts: false,
        viewUsers: false,
        modifyUsers: true
      }
    ]
  }               
}

Next, you should provide to this.handleChange function id of user, name of property we are changing, and current value:

renderRows(data) {
  return data.map((step, index) => 
    <tr key={index}>
      <td>{step['name']}</td>
      <td style={{align:"center", paddingLeft:"40px"}}>
        <Input type="checkbox"
          checked={step['viewAccounts']} 
          onChange={e => this.handleChange(step.id, 'viewAccounts', step['viewAccounts'])}/></td>
      <td style={{align:"center", paddingLeft:"40px"}}>
        <Input type="checkbox"
          checked={step['modifyAccounts']} 
          onChange={e => this.handleChange(step.id, 'modifyAccounts', step['modifyAccounts'])}/></td>
      <td style={{align:"center", paddingLeft:"40px"}}>
        <Input type="checkbox"
          checked={step['viewUsers']} 
          onChange={e => this.handleChange(step.id, 'viewUsers', step['viewUsers'])}/></td>
      <td style={{align:"center", paddingLeft:"40px"}}>
        <Input type="checkbox"
          checked={step['modifyUsers']}
          onChange={e => this.handleChange(step.id, 'modifyUsers', step['modifyUsers'])}/></td>
      <td style={{align:"center"}}>
        <Link to="/users"><i className="fa fa-edit fa-2x fa-fw" /></Link>
        <Link to="/users"><i className="fa fa-times-circle fa-2x fa-fw" /></Link></td>
    </tr>
  );
}

And lastly, in this.handleChange function, we should update particular user data according given values:

handleChange(id, name, value) {
  this.setState({
    users: this.state.users.map((user) => {
      if (user.id !== id) return user;

      // `Object.assign` function is used to return new modified object.
      return Object.assign({}, user, {
        // We should assign opposite to `value` variable value, as we are toggling permissions.
        [name]: !value
      });
    });
  });
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the quick reply. I tried what you suggested and it doesn't quite work. If the state of the permission is true it will set it to false, but it won't set false to true. I added console.log(value) in handleChange and it outputs "on" no matter if the box is checked or not. I am working on figuring out why.
I changed checked={step['viewAccounts']} to value={step['viewAccounts']} for all permissions and it now works properly. Thanks again for your help!
Actually what I did didn't fix it properly. On reload the checkboxes do not match which permissions are on or off they are just showing blank now because they don't have the checked prop anymore.
@JamesThibaudeau, ok, let's try to figure out. First, each input should have it corresponding checked property. You shouldn't set value={step['viewAccounts']} for all inputs. You need to return input's values as in answer. Also, I made a mistake, instead of e.target.value you should also provide to onChange function corresponding value from state. Check updated answer, now it correct.
That makes sense. Making your changes has it setting the state properly and displaying the view as well. Thanks for your help!
0

Check out the source code of this library to see how they manage the state of checkboxes. Perhaps you could even use it for your needs:

https://www.npmjs.com/package/react-checkbox-group

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.