React: Passing a div's id/class attribute into handleClick

React: Passing a div's id/class attribute into handleClick
0

#1

In my current calculator project (https://codepen.io/Crimson_Macaw/pen/JeMeEP) I’m trying to get the handleClick function to take in a div’s id as its argument. The snippet is below:


class Buttons extends React.Component{
  runParentHandleClick = () => {
    this.props.handleClick(id)
  }
  render(){
    return(
      <div className = "buttonRows">
    <div className = "rowOne" onClick = {this.runParentHandleClick} id = "nine" data-id = "nine">9</div>
        <div className = "rowOne" onClick = {this.runParentHandleClick} id = "eight" data-id = "eight">8</div>
        <div className = "rowOne" onClick = {this.runParentHandleClick} id = "seven">7</div>
        <div className = "rowOne" onClick = {this.runParentHandleClick} id = "six">6</div>
     
        </div>
    )
  }
}

This is the only way I’ve figured out how to pass a unique argument into the handleClick function so that it can then display the number in the calculator screen. The problem is that I don’t know how to write the syntax for it properly.

If anyone has any ideas on other ways that I could achieve this (without passing in the div’s id) I’m open to trying them out as well.


#2

Take a look at the official doc here.


#3

I’ve read through the official documents and they weren’t of any help.

I’ve spent several hours on this problem, going through the official docs, stack overflow and other places and still can’t get it to work. I could try scrapping this approach and copying the approach from the one provided in the main project example, but that would require a several hour redo again.


#4

In your hardcoded approach you can just pass any argument you wish (you don’t need id for that):

onClick={() => this.runParentHandleClick('5')}

In this way parent will know that 5 has been pressed. Is this what you’re trying to achieve?


#5

Semantically though, you might think of your buttons as inputs, each with certain value. And then you can do more generic:

onClick={(e) => this.runParentHandleClick(e.target.value)}

#6

It seems like you are doing this the wrong way, you should be rendering a list of objects instead of hardcoding all the items one by one. Here’s an example:

class Buttons extends React.Component {
 render() {
  const items = [
   {id: "six", number: 6},
   {id: "seven", number: 7},
   {id: "eight", number: 8}.
   {id: "nine", number: 9}
  ]

  <div className="buttonRows">
    {items.map(el => <div className="rowOne" key={el.id}>{el.number}</div>)}
  </div>
 }
}

I am not sure why you are using names for the ids, but this is how you render a list in React. Now you just have to create a method that gets the object that was clicked:

handleClick = event => {
 console.log(event.currentTarget)
}

At the end it would look like this:

class Buttons extends React.Component {

 handleClick = event => {
  console.log(event.currentTarget)
 }

 render() {
  const items = [
   {id: "six", number: 6},
   {id: "seven", number: 7},
   {id: "eight", number: 8}.
   {id: "nine", number: 9}
  ]

  <div className="buttonRows">
    {items.map(el => ( 
    <div className="rowOne" key={el.id} onClick={() => this.handleClick(item)}>
      {el.number}
     </div>
    ))}
  </div>
 }
}

If this is confusing to you I’d highly recommend you to read the React docs or relearn some of the React main concepts from whatever tutorial you are doing.


#7

@iiiked I’m doing this for the calculator project, and so I’m trying to pass the e.target.value into the handleClick function’s argument. The problem is that whenever I code it the way you described, nothing shows up.

@Gilbert1391 I tried doing it your way and its still not displaying any numbers that I input. Would you be able to take a look at my actual codepen linked above and see if it’s a problem elsewhere.

All the code you wrote made perfect sense and I’m sure my understanding of React is more or less up to par, it’s just the syntax that’s messing me up. Also, in the handleClick method you wrote event.currentTarget. Shouldn’t it be event.target.value?


#8

I just noticed there’s a syntax error in the array of objects, there’s a dot after the third object and it should be a comma.


#9

Yeah I fixed that already and it wasn’t the issue.


#10

Whenever the calculator first loads everything displays fine. But as soon as I press any of the keys then the zero disappears from the display and none of the other numbers show up.


#11

I forked your pen and made a few adjustments with the advice from the poster above.

I also edited your handleClick function.

Maybe this will help you out


#12

Thanks camelcamper, it does seem to help. I just wanted to know why you used the ES6 backticks and $ when defining the this.setState.input.

Also if you could walk me through the logic of your changes that would also help. In particular, why arguments are needed in both handleClick and runParentHandleClick and not just one of them.


#13

@camelcamper Another thing I can’t seem to understand from your changes is why something like onClick = {this.props.handleClick(“9”)} brings back an error and won’t get the number to display, but onClick = {() => this.props.handleNumbers(“9”)} will work. What does adding the () => into the onClick method actually do?


#14

In first example you’re passing the result of a function, not the function itself. Try typeof this.props.handleClick(“9”)


#15

it is an easier and nicer way to concatenate strings

//ES6
this.setState({
      input: `${this.state.input}${x}`
    })

//vanil JS
this.setState({
      input: this.state.input + x
    })

although it doesnt really look like it in your handleClick function, this example concatenating a date string would be much nicer with ES6 backticks

let currentDate = "Today is the " + this.date + " of " + this.month" + " " + this.year"
let currentDate = "Today is the ${this.date} of ${this.month} ${this.year}

Your runParentHandleClick is redundant because you are already passing the handleClick function from the parent to the <Button /> child component,

<Buttons handleClick = {this.handleClick}
          initialize = {this.initalize}
          handleNumbers = {this.handleNumbers}
          />

You are setting an attribute named handleClick and assigning it your function this.handleClick so it can be accessed in your child component as a prop with this.props.handleClick

So you can delete the runParentHandleClick and use the parents handleClick as a prop.

Then you are just passing the number/value of the button as a parameter into the function so it knows which number to update the state with, which then re-renders the screen component.

This is something I know works but I dont know why and have been meaning to learn, so cheers for the motivation

You are passing the number/value of the button as a parameter which forces us to use the parenthesis and this executes the function on render

onClick={this.handleClick('9')}

You need to pass a reference to the function so if you have a function with no parameters then you can simply write

onClick={this.renderNumber}

However you are passing an parameter so we can use an ES6 arrow function to wrap around an event handler and pass the parameter

() => this.handleClick('9')

The react docs on functions that @shimphillip posted explains this and has heaps of other useful info


#16

Actually you don’t need to pass the event object to the handleClick method as you just want the properties defined in your objects, specifically the id property. You just need to pass an argument as a reference to that object.

handleClick = item => {
 console.log(item)
}

I wrote the code in a rush with no testing and doing guessing work based on what you said, but it should work unless there is any other typo error.

Rect syntax is just JavaScript combined with HTML, it’s not something too unfamiliar. If it’s a bit confusing it’s probably because you are not used to OOP (Object Oriented Programming) and working with objects. I was there myself when I started out and then I decided to do a 6 hours course about OOP, after that I felt way more confident working with React. this is the course in case you are wondering, I highly recommend it.

I also noticed you were asking about template literals, which makes me think you are probably not too familiar with ES6 and that could lead to some difficulties. Before moving on I would encourage you to get familiar with some essential JS/ES6 features such as:

  • let/const
  • objects
  • this
  • arrow functions
  • destructuring
  • template literals
  • spread operator
  • classes
  • modules

After you have a basic knowledge of all the above, things are going to be way easier. I’m not saying this to sound condescending, I’m just trying to help you out, because we can give you the code you need, but you should be able to understand most of it, otherwise you won’t be able to come up with solutions yourself.

By the way, are you using CRA (Create React App) or codepen to build this project?, if it’s the latter, I would advise you to start using CRA to build React apps.

Anyways man, building is the best way to learn and that’s exactly what you are doing. If you have more questions I’ll be happy to help.


#17

I took a look at your pen, first thing I noticed is that it seems like something is not working because I press buttons and the value does not change at all. But more importantly, the handleClick method has way too many things going on, which makes your app fragile and hard to maintain. Your methods should always be specific, ideally each method should handle 1 thing, this way your code is easier to read and to maintain.

I think you need to refactor your code and break this app in small components. I’ve never made a calculator before, but right off the bat this is how my first approach would probably look like:

class App extends React.Component {
// No need to call constructor to initialize state anymore
  state {
   value: 0
 }

render() {
  return(
   <div className="container">
    <Input value={this.state.value} />
    <OperationButtons />
    <Buttons />
    <EqualButton />
   </div>
  );
 }
}

There could be typos as I typed all this directly on here, and of course you might need to change the CSS to make it look the same, but the important thing here is how we are breaking this calculator in small components.

You will pass props (properties) to each component based on the props they need. Take the Input component for example, what props does this component need?, well it needs the value property from the state to display in the UI that value as the input value, that is why in the code above I passed the value property from the state as a props.

const Input = ({ value }) => {
  return <input type="text" value={value} />
}

This Input component is a functional component, with this implementation whatever the value of the state is, that is what the value of this input element will be.

Now I don’t know if in codepen you can work with modules, if not, that’s why I recommended you to use CRA to build React apps, it’s tricky at first, but it’s better you get used to it from the beginning, besides it’s just better for so many reasons.

To continue with this implementation, you just need to break the problem into smaller problems, and create a method for each one of those problems. Just let me know if you have further questions.


#18

Thanks for all the responses and explanations. Yes I’m working on this in codepen and maybe I’ll check CRA to see if it would make any difference, but honestly codepen seems to be doing the same job as Visual Studio Code when it comes to coding React so I don’t see how CRA would be that much better.

Initially I did break my code down into smaller modules but then decided to go a different route.


#19

There are some benefits for using CRA over codepen, but if you don’t want to go through the installation and such, you can also use sandbox.io, it is definitely a better option than codepen as it allows you to work with modules.


#20

@Embustero

I took a look at your code, you have a typo when you are passing the initialize() method as a prop to your Buttons /> component, here’s the typo: initialize = {this.initalize}, it should be “this.initialize”.

I would probably define that method as “handleClear” and then the name as a prop would be onClear={this.handleClear) and follow naming convention. If you don’t follow naming convention then you will probably deal with plenty of bugs, you just experienced one in a relatively small app.