Reputation: 485
I'm mapping over an array of objects in the state and returning separate div elements containing information from each index. I'm trying to figure out the best way (or one at all) to attach an options list to each element and update the state based off of what is chosen. For instance, if the name of index 1 is "hannah" and the user selects "Terry," I want to change index 1's name to "terry."
Here's the code I have so far:
class App extends React.Component {
state = {
items: []
};
componentDidMount() {
this.setState({
items: [
{
name: "jacob",
hair: "brown",
sex: "male"
},
{
name: "hannah",
hair: "brown",
sex: "female"
}
]
});
}
handleChange = e => {
var x = Object.assign({}, this.state);
};
render() {
const { items } = this.state;
return (
<div>
{items.length &&
items.map((item, index) => (
<div className="row mt-5" key={index}>
<Item item={item} handleChange={this.handleChange} />
</div>
))}
</div>
);
}
}
const Item = ({ item, handleChange }) => (
<div className="col">
<div className="mt-5" value={item.name}>
{item.name}
</div>
<select onChange={handleChange}>
<option value="jacob">Jacob</option>
<option value="hannah">Hannah</option>
<option value="tom">Tom</option>
<option value="kim">Kim</option>
<option value="terry">Terry</option>
</select>
</div>
);
ReactDOM.render(<App />, document.getElementById("root"));
<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="root"></div>
I've started the handleChange fx by setting x to a copy of the state, that way I can indirectly update it later. But as soon as I did that, I realized I had no idea how to functionally target the index that I wanted to update.
Thanks to Tarik Lefi for providing some start-up code!
Upvotes: 2
Views: 168
Reputation: 31014
If you want to pass another parameter such as index
or id
you can use an inline anonymous function or you can convert Item
to a class
component that can have it's own handleChange
method.
This way, if you pass it another prop
such as for example indexId
, it can pass it back up via the handler.
The problem with inline handlers that created inside the render
block is that a new function is created on each render
and this may cause a performance hit.
This is why i prefer the component composition approach.
Here is your code with some modifications:
class App extends React.Component {
state = {
items: []
};
componentDidMount() {
this.setState({
items: [
{
name: "jacob",
hair: "brown",
sex: "male"
},
{
name: "hannah",
hair: "brown",
sex: "female"
}
]
});
}
handleChange = (value, indexId) => {
const { items } = this.state;
const nextItems = items.map((item,index) => {
if(indexId !== index) return item;
return {
...item,
name: value
}
});
this.setState({items: nextItems});
}
render() {
const { items } = this.state;
return (
<div>
{items.length &&
items.map((item, index) => (
<div className="row mt-5" key={index}>
<Item item={item} handleChange={this.handleChange} indexId={index} />
</div>
))}
</div>
);
}
}
class Item extends React.Component {
handleChange = ({target}) => {
const {handleChange, indexId} = this.props;
handleChange(target.value, indexId);
}
render() {
const { item } = this.props;
return (
<div className="col">
<div className="mt-5" value={item.name}>
{item.name}
</div>
<select onChange={this.handleChange}>
<option value="jacob">Jacob</option>
<option value="hannah">Hannah</option>
<option value="tom">Tom</option>
<option value="kim">Kim</option>
<option value="terry">Terry</option>
</select>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<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="root"></div>
EDIT
As a followup to your comment:
but the handleChange method in the Item component looks like a foreign language to me. Would you mind explaining what's going on here, or maybe linking to some reading material so I can become more familiar with it?
This is ES2015 destructuring syntax.
const {handleChange, indexId} = this.props;
its same as doing:
const handleChange = this.props.handleChange;
const indexId = this.props.indexId;
And this will extract the target property from the event passed to the handler:
handleChange = ({target}) => {
Instead of doing this:
handleChange = (e) => {
// e.target
Upvotes: 1