Thian Kian Phin
Thian Kian Phin

Reputation: 931

pass id through on click react.js

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

Answers (6)

Black
Black

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

Boky
Boky

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

mayid
mayid

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

cdagli
cdagli

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

syllabix
syllabix

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

Pranesh Ravi
Pranesh Ravi

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

Related Questions