1

I want a container with table that has one column "enabled" which can be toggled. I want to save the state of toggle in object I used to display the row (In the example below, I want to store it in enable attribute of student object). I have table and toggle button displaying properly. But the toggle button is not clickable/togglable and doesnt store the state. live code here.

Here is my code

class student {
  constructor(id, name, age,email,enable) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.email = email;
    this.enable = enable;
  }
}


const Switch = ({ isOn, handleToggle}) => {
    return (
    <>
      <input
        checked={isOn}
        onChange={handleToggle}
        className="react-switch-checkbox"
        id={`react-switch-new`}
        type="checkbox"
      />
      <label
        className="react-switch-label"
        htmlFor={`react-switch-new`}
      >
        <span className={`react-switch-button`} />
      </label>
    </>
  );
};




class Table extends React.Component {
  constructor(props) {
      super(props)
      this.state = {
         students: [ new student(1, 'foo', 12, '[email protected]', true), 
                    new student(2, 'bar', 22, '[email protected]', false),
                    new student(3, 'foobar', 44, '[email protected]', true),
                    new student(4, 'foofoo',57,  '[email protected]', false)
                    ]
      }
   }



   renderTableHeader() {
      let header = Object.keys(this.state.students[0])
      return header.map((key, index) => {
         return <th key={index}>{key.toUpperCase()}</th>
      })
   }

   renderTableData() {
      return this.state.students.map((student, index) => {
         const { id, name, age, email, enable } = student //destructuring
         //const [value, setValue] = useState(false);
         return (
            <tr key={id}>
               <td>{id}</td>
               <td>{name}</td>
               <td>{age}</td>
               <td>{email}</td>
             <td><Switch
        isOn={enable}
        handleToggle={() => student.enable=!enable}
      /></td>
            </tr>
         )
      })
   }

   render() {
      return (
         <div>
            <h1 id='title'>React Dynamic Table</h1>
            <table id='students'>
               <tbody>
                  <tr>{this.renderTableHeader()}</tr>
                  {this.renderTableData()}
               </tbody>
            </table>
         </div>
      )
   }
}

ReactDOM.render(<Table />, document.getElementById('root'));  

Here is the screenshot of how page looks enter image description here

1 Answer 1

3

The issue is, you cannot directly change the state like,

handleToggle={() => student.enable=!enable}

This will never change your state. To change your state you must use setState.

You should have a dedicated function to toggle your state,

handleToggle = (id) => {
   this.setState({
      students: this.state.students.map(std => {
        if(parseInt(std.id) === parseInt(id)){  //parse id to integer for safer side
          return {...std, enable: !std.enable}
        }else{
          return std;
        }
      })
   })
}

You should provide this function to your Switch. Also provide id for updation.

<Switch
    isOn={enable}
    id={id}   //provide id for updation
    handleToggle={this.handleToggle}   //provide function reference
/>

You need to make some changes to your Switch,

const Switch = ({ isOn, handleToggle, id}) => {   //take prop id
  return (
    <>
      <input
        checked={isOn}
        onChange={() => handleToggle(id)}   //call handleToggle using id
        className="react-switch-checkbox"
        id={`react-switch-new${id}`}        // Make it unique by adding id
        type="checkbox"
      />
      <label
        className="react-switch-label"
        htmlFor={`react-switch-new${id}`}   // Make it unique by adding id
      >
        <span className={`react-switch-button`} />
      </label>
    </>
  );
};

Demo

Sign up to request clarification or add additional context in comments.

6 Comments

what does return {...std, enable: !std.enable} mean?
Here we are updating state value based on id. So when parseInt(std.id) === parseInt(id) is true, we need to update that object with new value else we are returning the existing object. {...std, enable: !std.enable} means, destructure the object (if id matches) and update the value of enable key.
When we say destructuring, {...std} is equivalent to {"id":4,"name":"foofoo","age":57,"email":"[email protected]","enable":false} (just an example, actual object will be based on condition) and {...std, enable: !std.enable} means update the enable key (from false to true or vice-versa).
Because in HTML itself id should be unique. As you are adding Switch in a map all the Switches gets the same id. If we have multiple same id then it is very difficult identify which switch is getting clicked and your toggle function will not get execute.
I agree id should be unique, but since I don't use id for any css or any other lookups, I left it as it is. But as a good practice, it should be made unique
|

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.