0

I created a React app. It works as expected in IE, Opera, Chrome, FF. I have problems with rendering one component in Edge after state update. I can't help myself with React Devtools for Chrome, because it all works there as it should.

My component is a combination of two input fields. One is number input, which can be manipulated by user, the other displays the same number value, but formatted as currency string. When user clicks on the field, the field with formatted value hides and shows the non-formatted input field. When user moves away from editable input, the before hidden input shows again.

Because state gets changed every time onchange events is triggered, I expect, that the other input would also update on each and every onchange. Is this because it is hidden?

Here is the code, I tried to simplify it as much as possible:

class MasterComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            Rows: [
                { Id: 1, Name: 'Row 1', Price: 11 },
                { Id: 2, Name: 'Row 2', Price: 21 },
                { Id: 3, Name: 'Row 3', Price: 13 }
            ]
        }
        this.changeObjectValue = this.changeObjectValue.bind(this);
    }
    render() {
        return <MCVE changeObjectValue={this.changeObjectValue} rows={this.state.Rows}></MCVE>
    }

    changeObjectValue(rowId, propertyName, value) {
        // define which list user wants to change
        let list = this.state['Rows'];
        let changedList = [];
        // change value
        list.forEach(element => {
            if (element.Id == rowId) {
                element[propertyName] = value;
            }
            changedList.push(element);
        });
        // dynamically set objct name frmo parameters
        this.setState({ Rows: changedList });
    }
}


class MCVE extends React.Component {
    render() {
        return <table>
            <thead>
                <tr>
                    <th>Id</th>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
            </thead>
            <tbody>
                {this.props.rows.map((r, index) =>
                    <tr key={r.Id}>
                        <td>
                            <input readOnly value={r.Id}></input>
                        </td>
                        <td><input name="Name" onChange={event => this.changeInput(event, r.Id)} value={r.Name}></input></td>
                        <td>
                            <input name="Price" className="edit" type="hidden"  onBlur={event => toggleEditDisplay(event)} onChange={event => this.changeInput(event, r.Id)} value={r.Price}></input>
                            <input name="Price" className="display" readOnly onClick={event => toggleEditDisplay(event)} value={formatMoney(r.Price)}></input>
                        </td>
                    </tr>
                )}
            </tbody>
        </table>
            ;
    }
    changeInput(event, rowId) {
        let propertyName = event.target.name;
        this.props.changeObjectValue(
            rowId,
            propertyName,
            event.target.value);
    }
}

function toggleEditDisplay(event) {
    let el = event.target;

    // if has class 'edit' else has class 'display'    
    if (el.className.split(/\s+/).filter(e => e == 'edit').length > 0) {
        let elToToggle = el.parentElement.getElementsByClassName('display')[0];
        el.type = "hidden";
        elToToggle.type = "text";
    }
    else {
        let elToToggle = el.parentElement.getElementsByClassName('edit')[0];
        el.type = "hidden";
        elToToggle.type = "number";
    }
}
function formatMoney(amount, decimalCount = 2, decimal = ",", thousands = ".", currencySymbol = '€', ignoreCurrencySymbol = false) {
    try {
        decimalCount = Math.abs(decimalCount);
        decimalCount = isNaN(decimalCount) ? 2 : decimalCount;

        const negativeSign = amount < 0 ? "-" : "";

        let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(decimalCount)).toString();
        let j = (i.length > 3) ? i.length % 3 : 0;

        return negativeSign + (j ? i.substr(0, j) + thousands : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) + (decimalCount ? decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "") + (ignoreCurrencySymbol ? "" : currencySymbol);
    } catch (e) {
        console.log(e)
    }
};

ReactDOM.render(<MasterComponent />, document.getElementById('app'));

Minimum HTML:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>

    <body>  
        <div id="app"></div>
        <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
        <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

        <script src="reactScript.js"></script>
    </body>    
</html>
gtu
  • 707
  • 1
  • 10
  • 22
  • 1
    You need to show some code so we can take a look at it. – Domino987 Aug 22 '19 at 11:12
  • Since it contains some functions (such as format the value ), can you post the Enough code to reproduce the problem as in [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve). – Zhi Lv Aug 22 '19 at 12:40
  • i hope it is everything now. – gtu Aug 22 '19 at 13:07
  • I am looking right now in devtools, that `value` attribute has correct data inside, but what is displayed is the old value. I don't know why it does not re-render. – gtu Aug 22 '19 at 13:43

1 Answers1

1

After testing, it seems that the issue is related to the type="hidden", we could modify the code and use CSS display attribute to hide or display the input element. Please check the following steps:

  1. Remove the 'type="hidden"' from the first input TexBox.
  2. Add the following css style in the index.css file:

    .edit{
      display:none;
    }
    
  3. Modify the toggleEditDisplay function:

    function toggleEditDisplay(event) {
      let el = event.target;
    
      // if has class 'edit' else has class 'display'    
      if (el.className.split(/\s+/).filter(e => e == 'edit').length > 0) {
          let elToToggle = el.parentElement.getElementsByClassName('display')[0];
          el.style.display ='none';
          elToToggle.type = "text";
          elToToggle.style.display ='block';
      }
      else {
          let elToToggle = el.parentElement.children[0];
          //el.type = "hidden";
          el.style.display ='none';
          elToToggle.type = "number";
          elToToggle.style.display ='block';
      }
      el.focus();
    }
    

The result in the Edge browser as below:

enter image description here

Zhi Lv
  • 18,845
  • 1
  • 19
  • 30
  • In this case you can also omit setting the `number` and `text` type of input when toggling the display. They can be defined in the html tag. – gtu Aug 26 '19 at 09:31