Reputation: 931
Below is my partial code but my question is very simple, how can I get says data-id="1" to my function when user clicked on the li
?
render(){
return(
<ul id="todo">
{this.state.items.map((item,i) =>
<li className='list-group-item' key={i} data-id={item.id}>{item.name}
<button onClick={//how to pass item.id to my function?}>X</button>
</li>
)}
</ul>
)
}
Upvotes: 18
Views: 97758
Reputation: 10397
Mayid is correct that it is not good to declare or bind functions in render method if the function is passed into other component as a props.
Unfortunately currentTarget didn't work for me.
I have used getAttribute function of event.target. This doesn't cause unnecessary rerenders.
class App extends React.Component {
handleClick = (event) => {
console.log(event.target.getAttribute('index'))
}
render() {
return (
<button index="1" onClick={this.handleClick}>
Click me
</button>
);
}
}
Upvotes: 3
Reputation: 12064
You can do this as follows :
class Test extends React.Component {
constructor(props){
super(props);
this.state = {
items: [
{item: "item", id: 1},
{item1: "item1", id: 2}
]
}
}
handleClick(id, e){
alert(id);
}
render(){
return(
<ul id="todo">
{this.state.items.map((item,i) =>
<li className='list-group-item' key={i} data-id={item.id}>{item.name}
<button onClick={this.handleClick.bind(this, item.id)}>X</button>
</li>
)}
</ul>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is jsfiddle.
Upvotes: 6
Reputation: 1775
In my opinion, you shouldn't declare functions, nor bind methods within render method. Neither of these:
onClick={(e) => this.handleClick(e, item.id)}
onClick={this.handleClick.bind(this, item.id)}
I know it's plenty of tutoriales showing that syntax. But there's also a considerable number of blog posts warning about why that's not a good idea. Basically, you are creating a new function on each render.
Go check the manual: https://reactjs.org/docs/handling-events.html
And I'm aware that in the last two examples it does create functions on render. But react manual also shows this example and says:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
The problem with this syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine. However, if this callback is passed as a prop to lower components, those components might do an extra re-rendering. We generally recommend binding in the constructor or using the class fields syntax, to avoid this sort of performance problem.
BETTER SOLUTION
So, if you only need to pass one value, then check out the other examples in the manual. Basically you can bind the method in the constructor (or use an experimental syntax to avoid that step).
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
And how would you get the id/value that you are trying to get? See this example:
class App extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick({currentTarget}) {
console.log(currentTarget.value) // e.currentTarget.value would be equivalent
}
render() {
return (
<button value="here!" onClick={this.handleClick}>
Click me
</button>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
body {
padding: 5px;
background-color: white;
}
<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>
So, if you are using buttons or any form element (accepting a value), you may definitively consider this syntax.
Upvotes: 10
Reputation: 1578
Below is a running sample;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [{
id: 0,
name: "Buy milk"
}, {
id: 1,
name: "Write unit tests"
}, {
id: 2,
name: "Cook a meal"
}]
}
this.handleClick = this.handleClick.bind(this);
}
handleClick(value) {
console.log(`${value} clicked`);
}
renderTodos() {
return this.state.items.map((item, idx) => {
return ( < li className = 'list-group-item'
key = {
idx
} > {
item.name
} < button onClick = {
() => this.handleClick(item.id)
} > X < /button>
</li >
)
})
}
render() {
return ( < ul id = "todo" > {
this.renderTodos()
} < /ul>
)
}
}
ReactDOM.render(
<App/ > ,
document.getElementById('react_example')
);
<!DOCTYPE html>
<html>
<head>
<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>
<meta charset="utf-8">
</head>
<body>
<div id="react_example"></div>
</body>
</html>
Upvotes: 2
Reputation: 2295
Since you are already using ES6 - might be a little cleaner to use an arrow function here:
render(){
return(
<ul id="todo">
{this.state.items.map((item,i) =>
<li className='list-group-item' key={i} data-id={item.id}>{item.name}
<button onClick={() => this.yourfunc(item.id)}>X</button>
</li>
)}
</ul>
)
}
Upvotes: 41
Reputation: 19113
You can use bind()
to do this.
render(){
return(
<ul id="todo">
{this.state.items.map((item,i) =>
<li className='list-group-item' key={i} data-id={item.id}>{item.name}
<button onClick={yourfunc.bind(this, item.id)}>X</button>
</li>
)}
</ul>
)
}
Your function will receive item.id
as the first parameter
Upvotes: 16