Search code examples
javascriptreactjshtml-tableconditional-statementsreact-state-management

How can I create a table with conditionally editable input/cells using React?


I have a table like this:

enter image description here

When a user clicks on an Edit button, an <input> should appear in its place.

If a user clicks on another Edit button, this one will also be replaced with an <input>, and the previous <input> should disappear and show an Edit button again.

In short, only one field can be in edit mode at a time.

This is my initial state:

state = {
    editnameEnable: false,
    editemailEnable: false,
    editaddressEnable: false,
    edittelephone_noEnable: false,
}

This is my edit() method:

edit = value => {
    var address_element = ['name','address','email','telephone_no']; 
    address_element = address_element.filter(element => element !== value); 
    address_element.map( val => this.setState({[`edit${val}Enable`]: false}));

    this.setState({[`edit${value}Enable`]: true}, ()=>{
        console.log(this.state);
    });
}

This is part of the JSX inside my render method:

<td>{
    this.state[`edit${this.props.item}Enable`] ? ( <input type="text"/> ) : (
        <span
            className="edit"
            onClick={ () => this.edit(this.props.item) }>Edit</span>
    )
}</td>

The issue is that when I click on an Edit button, the <input> appears, but when I click another Edit button, the previous <input> does not disappear.


Solution

  • What about using a single editableField property that is initially set to null?

    Then, when you click on an Edit button, you set editableField to the name of that field, and inside render you check, for each field, whether editableField matches its name or not to render an Edit button or an input field.

    Something like this:

    class FieldCell extends React.Component {
    
      constructor(props) {
        super(props);
      }
      
      focusField = (element) => element && element.focus();
      
      render() {
        const editElement = this.props.isEditable ? (
          <input type="text" ref={ this.focusField }/>
        ) : (
          <button onClick={ () => this.props.onEdit() }>EDIT</button>
        );
        
        return (<td className="textLeft">{ editElement }</td>);
      }
    }
    
    class UserData extends React.Component {
    
      constructor(props) {
        super(props);
        
        this.state = {
          editableField: null,
        };
      }
      
      render() {
        const editableField = this.state.editableField;
        
        const rows = ['Name', 'Address', 'Telephone No.', 'E-Mail'].map((field) => {
          const isEditable = field === editableField;
          
          return (
            <tr key={ field }>
              <td>{ field }</td>
              <FieldCell isEditable={ isEditable } onEdit={ () => this.setState({ editableField: field })}></FieldCell>
            </tr>
          );
        });
    
        return (<table>{ rows }</table>);
      }
    }
    
    ReactDOM.render(<UserData />, document.getElementById('app'));
    body {
      font-family: monospace;
      font-size: 13px;
    }
    
    table {
      border: 2px solid #000;
      border-collapse: collapse;
      text-align: right;
      width: 100%;
    }
    
    td {
      border: 2px solid #000;
      padding: 4px;
      width: 50%;
      overflow: hidden;
    }
    
    .textLeft {
      text-align: left;
      user-select: none;
    }
    
    button,
    input {
      border: 2px solid black;
      padding: 4px 8px;
      margin: 0;
      font-family: monospace;
      font-size: 13px;
      box-sizing: border-box;
      background: none;
      outline: none;
    }
    
    button:hover,
    button:focus,
    input:hover,
    input:focus {
      border-color: blue;
      color: blue;
    }
    
    button {
      font-weight: bold;
    }
    
    input {
      width: 50%;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    
    <div id="app"></div>