Reputation: 129
I have an object model pattern as below
this.state = {
data: {
audi: {
engine: '2.5',
gearbox: 'auto',
fuel: 'petrol'
},
bmw: {
engine: '3.0',
gearbox: 'auto',
fuel: 'petrol'
},
merc: {
engine: '6.3',
gearbox: 'manual',
fuel: 'petrol'
}
}
}
My goal is to:
I haven't used an Array, as it has to look like above object pattern when posting it.
At the moment here is how I am trying to do this:
//To render
Object.entries(this.state.data).map((x,index) =>
<p>x.key</p> //Trying to reach 'audi' with x.key
// Render each KPV in object
{x.key}: <input key={index} onChange={this.handleChange} value={x.value}/>
)
Then to update the corresponding values in the same state model:
// To update the state model with changed values
setData = (key, val) => {
this.setState(state => ({
data: {
...state.data,
obj: { [key]: val },
//Don't know how to map to state properly!
}
}));
};
// Handle input change
handleChange = key => e => {
this.setData(key, e.target.value);
};
Please could anyone advise me on this approach, many thanks :)
Upvotes: 4
Views: 3837
Reputation: 136074
First you need to get the rendering right, which means looping over all the keys (car makers) and then all the property/value pairs. You can use destructuring arrays to make this easier
Object.entries(someObject).map( ([key,value]) => .... )
Heres rendering:
render() {
return Object.entries(this.state.data).map(([maker, props]) => {
return (
<div>
<h2>{maker}</h2>
{Object.entries(props).map(([key, value], index) => {
return (
<div>
{key}:{" "}
<input
key={index}
onChange={this.handleChange(maker, key)}
defaultValue={value}
/>
</div>
);
})}
</div>
);
});
}
You'll note I changed your handleChange
method to pass the car maker and the property being edited - this can be passed to setData
:
// To update the state model with changed values
setData = (maker, key, val) => {
this.setState((state) => ({
data: {
...state.data,
[maker]: {
...state.data[maker],
[key]: val
}
//Don't know how to map to state properly! Now you do!
}
}));
};
// Handle input change
handleChange = (maker, key) => (e) => {
this.setData(maker, key, e.target.value);
};
Live working example: https://codesandbox.io/s/react-playground-forked-dw9sg
Upvotes: 3
Reputation: 516
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
data: {
audi: { model: "A7", transmission: "AT" },
merc: { model: "GLA", transmission: "MT" },
bmw: { model: "M3", transmission: "AT" },
},
}
}
update(make, detail, value) {
let data = this.state.data
data[make][detail] = value
console.log(`Updating ${make} ${detail} with ${value}`)
this.setState({ data: data })
}
handleChange(make, detail) {
return e => { this.update(make, detail, e.target.value) }
}
renderDetails(details) {
return details.map(d => (<div><span>{d.key}:</span> <input value={d.model} onChange={this.handleChange(d.key, "model")} /> <input value={d.transmission} onChange={this.handleChange(d.key, "transmission")} /></div>))
}
render() {
const details = Object.entries(this.state.data).map(d => ({ key: d[0], ...d[1] }))
return <div>{this.renderDetails(details)}</div>
}
}
ReactDOM.render((<App />), document.getElementById("app"))
body { font-family: monospace; }
span { display: block; font-weight: bold; }
input { width: 50px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Upvotes: 1
Reputation: 666
You are not passing key and event object from the input callback in the first place, your code should be like,
The display logic looks wrong to me,
Object.entries(this.state.data).map((x, index) =>
/*
here x is an array and it looks like
["audi", {engine: '2.5', gearbox: 'auto', fuel: 'petrol}
*/
// You need to access the data in the second index
// if you want to print each value of the object
<p>x[1].engine</p>
<p>x[1].gearbox</p>
<p>x[1].fuel</p>
// Render each KPV in object
{x[0]}: <input key={index} onChange={(e) => this.handleChange(x[0], e)} value={x[1].engine}/>
)
and handleChange
callback should look like, don't curry them.
handleChange = (key, e) => {
this.setData(key, e.target.value);
};
And I would suggest using a utility like castArray from lodash, https://lodash.com/docs/4.17.15#castArray to convert the object to an array so that you can easily work with it.
Upvotes: 2