3

I'm mapping data from the api response and rendering multiple divs out of it. Along with that I'm assigning a unique id from the response to the id attribute of each div like this:

...lists.map(list => {
  return (
  <div className='one' key={list.id} id={list.id} onClick={this.handleClick}>
    <div className='two'>
      <p>Hello World</p>
      <span>Foo Bar</span>
    </div>
  </div>
)
})

handleClick = (e) => {
  console.log(e.target.id)
  // other stuff
}

The Problem:

Whenever the outer div (className='one') is clicked the console logs undefined. However, if I assign the id value to the inner div (className='two') it logs the value of id only if the click is made within the dimensions of the inner div. Same is the case with the <span> and <p> tags.

Basically, the onClick returns a different target on clicking different html elements.

Expected result:

Clicking the parent div or anywhere inside that div should always return the value of the id attribute of the parent div.

m-ketan
  • 1,258
  • 2
  • 17
  • 23

5 Answers5

7

The thing is when you define onClick on the topMost parent, you need to use e.currentTarget.id instead of e.target.id since e.target will give you the element on which you clicked rather then the parent on which onClick listener is defined

class App extends React.Component {
state = {
  lists: [{id: 1}, {id: 2}, {id:3}]
}
render() {
  return (
    <div>
      {this.state.lists.map(list => {
          console.log(list.id)
          return (
          <div className='one' key={list.id} id={list.id} onClick={this.handleClick}>
            <div className='two'>
              <p>Hello World</p>
              <span>Foo Bar</span>
            </div>
          </div>
        )
       })
       }
    </div>
  )
}

handleClick = (e) => {
  console.log(e.currentTarget.id)
  
  // other stuff
}

}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="app"></div>
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • Thanks Shubham, just exactly what I wanted. – m-ketan Oct 09 '17 at 09:45
  • Check out this answer too, It could help https://stackoverflow.com/questions/45053622/how-to-avoid-binding-inside-render-method/45053753#45053753 – Shubham Khatri Oct 09 '17 at 09:52
  • Went through it all and I think it'll be the best solution for handling this the correct way. Creating a separate component and passing id as props would not only be a cleaner but also an efficient solution. – m-ketan Oct 09 '17 at 10:04
3

Ok, the problem isn't Reactjs, the problem is the event target.

You are using e.target when you have to use event.currentTarget, here is the difference.

  • target is the element that triggered the event (e.g., the user clicked on)
  • currentTarget is the element that the event listener is attached to.

Let see this in an example:

let tacos = [{
  person: "John",
  ingredient: 'Guacamole'
 }, {
  person: 'Sally',
  ingredient: 'Beef'
 }];

class App extends React.Component {

  render() {
    return (
      <div>
        <h3>List of tacos:</h3>
        <TacosList tacos={tacos} />
      </div>
    );
  }
}

class TacosList extends React.Component {
 constructor(props) {
   super(props);

    this.handleClick = this.handleClick.bind(this);
  }
 
  handleClick(event) {
   const currentTarget = event.currentTarget;
    
    console.log(event.target);
    console.log(currentTarget.id);
  }
  
  render() {
    return (
      <div>
        {this.props.tacos.map((taco, index) => (
     <div className="one" id={`reference-${index}`} key={index} onClick={this.handleClick}>
           <p>{taco.person}: {taco.ingredient}</p>       
     </div>
        ))}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
.one {
  padding: 20px;
}
<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>

Note that you can click on the div and p element and both will trigger the event, in the case of p it will propagate the event up to the div , therefore, it's when the event target changes

Jose Paredes
  • 3,882
  • 3
  • 26
  • 50
1

In the constructor, just put this line:

constructor() {
     this.handleClick = this.handleClick.bind(this);
}

After this, you will be able to access the ref to the element,

For more detail please go through this link : https://reactjs.org/docs/handling-events.html

Vivek Doshi
  • 56,649
  • 12
  • 110
  • 122
0

try to set event as function param

handleClick = (e) => {
  console.log(e.target.id)
  // other stuff
}
Sasha B.
  • 9
  • 5
-2

if you are already using ES6

I would change the code a bit, so it will easier to understand and work with no special glitches with events target and attributes

lists.map(list => {
    return (
       <div 
           className='one' 
           key={list.id} 
           onClick={this.handleClick.bind(this, list.id)

           <div className='two'>
             <p>Hello World</p>
             <span>Foo Bar</span>
           </div>
       </div>
    )
 })

 handleClick = (listId) => {
   console.log(listId)
   // other stuff
 }

as you can see here, I just call the method with the list id and I'm done

Tzook Bar Noy
  • 11,337
  • 14
  • 51
  • 82
  • anonymous functions causes re-render all the time since it will return a new reference every time this component is rendered – Jose Paredes Oct 09 '17 at 08:52
  • @JoseAPL now I removed the anonymous function. Dont see a reason to downvote it because of that. But now it wont rerender – Tzook Bar Noy Oct 09 '17 at 08:55
  • `this.handleClick.bind` will also cause the same effect cause you are binding a new reference, it has to be an internal function bound once, I do this because this causes performance issues for React projects! – Jose Paredes Oct 09 '17 at 08:58
  • You shouldn't be doing this since bind returns a new function everytime. See this question https://stackoverflow.com/questions/45053622/how-to-avoid-binding-inside-render-method/45053753#45053753 – Shubham Khatri Oct 09 '17 at 09:24