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>