Reputation: 6300
Warning: Severe React beginner.
I have a class:
export default class ItemsView extends React.Component {
//...
render() {
return (
<div>
<Container>
<Grid container>
<Grid item xs={4}>
<ul style={{listStyleType:"none"}}>
{
this.state.items.map((item) => {
return <li key={item.number} style={{cursor: "pointer"}}><Item onClick={this.handleSelected(item)} value={item.timestamp}/></li>
})
}
</ul>
</Grid>
<Grid item xs={4}>
<ItemDetail item={this.selected} />
</Grid>
</Grid>
</Container>
</div>
)
}
}
handleSelected(item) {
console.log("handle");
this.selected = item;
}
What I want is that when I click on the Item
div, which is rendered dynamically as a list of elements, the details of the item would appear in the ItemDetails
component.
Apart from the fact that probably my design isn't really "React"y, why does the handleSelected
get called when iterating, and not when I click on it?
Upvotes: 4
Views: 664
Reputation: 25842
Aside from the answer Vincenzo posted, you need to also use component state here. A handler that updates a property on the class will not result in a new render cycle. I can see that you are using this.selected
in the render as a prop to ItemDetail
<ItemDetail item={this.selected} />
This is problematic as changing the value of this.selected
will not trigger a new render. This is a great use case to use component state
export default class ItemsView extends React.Component {
state = { selected: null, items: [] }
handleSelected = (selected) => (event) => {
// ----------------------^------------^----
// return a function in the handler to pass a callback to the click handler
console.log("handle");
this.setState({ selected });
// -----------^------------^----
// set the selected item on component state when clicked
}
//...
render() {
return (
<div>
<Container>
<Grid container>
<Grid item xs={4}>
<ul style={{listStyleType:"none"}}>
{ this.state.items.map((item) => <li key={item.number} style={{cursor: "pointer"}}>
<Item onClick={this.handleSelected(item)} value={item.timestamp}/>
// --------------------------------------------^---------------
// no change necessary here since the handler returns a function.
</li>
)}
</ul>
</Grid>
<Grid item xs={4}>
<ItemDetail item={this.state.selected} />
// --------------------------------------^---------------
// reference state here
</Grid>
</Grid>
</Container>
</div>
)
}
}
Upvotes: 1
Reputation: 697
You are mapping over a list of items and want to pass the item to a handler callback (handleSelected
in this case). You usually would pass that through by invoking the function with parenthesis ()
. However, the side effect of this is the function is immediately executed. To fix that you can place it inside an arrow function, allowing it to execute on the click instead.
Hence: onClick={() => this.handleSelected(item)}
Upvotes: 0
Reputation: 195
You are invoking the function rather than passing a function reference to be executed on click. You should either define the handler to return a function or use a lambda / fat arrow function in the click handler
onClick={() => this.handleSelected(item)}
remember, this.handleSelected
is the functions reference. this.handleSelected()
is the return value of an already invoked function.
Upvotes: 4