React getting the ID of clicked button - javascript

I have 5 clickable links, each with unique ID. I am trying to get the ID of the link that was clicked. I have tried two methods but neither work as I would like.
Inside render I have:
this.state.data.map((dynamicData, key) =>
<a href={"/#/template"} id={dynamicData.id} onClick={() => this.reply_click(this.id)}>{dynamicData.name}</a>
Edit</button>
Basic method that returns undefined. What am I doing wrong?:
reply_click(clicked_id) {
console.log(clicked_id);
}

Using an arrow function will maintain the context of this from the outside, which won't be the this you want. You could try to use a regular function (function () { }) instead, but I'm not sure if this will be what you want or not there either.
You can, however, definitely use e.target.id to get what you want:
onClick={e => this.reply_click(e.target.id)}
That said, you should really avoid creating functions inside of things as it can create significant performance issues. It's much better to just create a function and pass it in:
// somewhere above
const handleClick = e => this.reply_click(e.target.id);
// attribute
onClick={handleClick}

you can do this
<a href={"/#/template"} id={dynamicData.id} onClick={() => this.reply_click(dynamicData.id)}>{dynamicData.name}</a>
or another way
<a href={"/#/template"} id={dynamicData.id} onClick={this.reply_click}>{dynamicData.name}</a>
reply_click(event) {
console.log(event.target.getAttribute('id'))
}

this.reply_click(this.id)
this is your component scope, which means every attribute you defined in your component can be accessed by this. Probably you did not define id in this component.
You probably wanna do
this.reply_click(dynamicData.id)
instead.

The this inside the arrow function is the same as the this outside it. The onclick is already aware of the correct this, so use it like this:
onClick={this.reply_click(this.id)}

Try this code. The code you have provided doesn't work because this is bound to the current React component, instead you could access id of the clicked element using e.target.id:
this.state.data.map((dynamicData, key) => {
<a href="/#/template" id={dynamicData.id} onClick={(e) => this.reply_click(e.target.id)}>{dynamicData.name}</a>
<button type="button" class="btn-lisa">Edit</button>
})
Unrelated, but still I want to note that you don't have to use <a href={"blah"} instead you can use <a href="blah" because the last one uses a string contant.

Related

Reactjs - How to avoid creating a new clickhandler function in each render

In my react component on a button click, i am passing a parameter to my click handler exactly like this
<a
id={`_primaryAction_${messageObject.id}`}
href="#"
class='message'
onClick={(e: MouseEvent) =>
this.handleClick(e, messageObject)
}
>
I have a usecase where my props are changing and re render is happening . so in each new render this click handler new instance will create. Is there a way to avoid this ?
Edited: removed id and passing wholeObject as it is my use case. Yes this is in loop . This a tag will create for the array of messages.
First of all, do more research to see if the re-rendering is indeed a cause for concern, as it might not be such a big deal performance-wise.
As a solution, you could create another component which you pass the object.
const ActionLink = (props) => {
const {
handleClick,
messageObject,
...otherProps
} = props;
const clickHandler = React.useCallback((e: MouseEvent) => {
handleClick(e, messageObject);
}, [handleClick, messageObject]);
return <a
{...otherProps}
onClick={ clickHandler }
/>;
}
export default ActionLink;
And in your case, you can use it like the following (instead of the a)
<ActionLink
id={`_primaryAction_${messageObject.id}`}
href="#"
class="message"
messageObject={messageObject}
handleClick={this.handleClick} >...</ActionLink>
And if required, you can further protect against re-renders by passing it through React.memo
export default React.memo(ActionLink);
Lastly as an alternative, you could do as others have suggested and provide the id to an attribute of the link, and use that inside the handleClick method to retrieve the correct message from the list
something like
<a
id={`_primaryAction_${messageObject.id}`}
href="#"
class='message'
data-message-id={messageObject.id}
onClick={this.handleClick}
>
and in your handleClick
handleClick(e){
const messageId = e.target.getAttribute('data-message-id');
// assuming your message list is named messageList
// adjust accordingly
const message = messageList.find(({ id }) => id === messageId);
// ... do what you were already doing with the message here
}
checkout useCallback
useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders
https://reactjs.org/docs/hooks-reference.html#usecallback
I think you are using a class component
since you want to pass an object which I think is coming dynamically and not some constant in component (i.e. object is part of a map) and also don’t want to create a new function on every render I would suggest set your button attribute's value as the value of your object and you can access it e.target.value and bind the method than using the inline callback
and it will not create a new function now here's the working example
I see you're using class component. In that case, just move the handler into a separate function.
class MyComponent extends React.Component {
handleClick = (e) => {
this.deleteRow(id, e)
}
render() {
return <button onClick={this.handleClick}>Delete Row</button>
}
}

React: Passing parameters in Dumb Component Functions

So I am in the process of working on a sort of "intermediate" level react project. I know the basics, but don't know best practices on some things.
Lets pretend I am passing a function to a "Dumb" component, in this dumb component is a button that is a callback to a parent function Editname which looks like this:
editName = (id) => {
console.log(`Name edited for ${id}`);
}
In the "Dumb" component there is a button that calls this since it's being passed as a prop from it's parent:
<button type="input" onClick={props.editName}>Edit</button>
However the problem is, I need to pass along the id as well to the callback function (I get the id as a prop in the dumb component as well). What's the best way to go about this? I know one option is:
{()=> {props.editName(props.id)} but i've been told this is a bad practice because the function will get created everytime. So what is the "proper" way to do this? Or do I need to make it as a class and handle it as a callback to another function within the class?
To avoid creating the function everytime, you can attach an identifier to the target element using data-* attributes and then make use of it further.
For example:
<button type="input" onClick={props.editName} data-id="edit-button">Edit</button>
And then, in the function, you can have this:
editName = event => {
const id = event.target.getAttribute("data-id");
console.log(`Name edited for ${id}`);
};
You can very well take the data-id from the props:
<button type="input" onClick={props.editName} data-id={props.id}>Edit</button>
How you would want to manage the data-id attribute will depend upon the use case.
This might not be the proper way, as OP has asked, but it does reduce the number of functions created everytime.
For good practice you should use destructuring in your code like...
const { editName, id } = props;
<button type="input" onClick={editName} data-id={id}>Edit</button>
For destructuring practices folllow this link.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

Calling a method vs using a function to call a method

Suppose we have a method inside a class like this
class Blog extends Component {
postClicked = (id) => {
this.setState({selectedPostId: id})
}
render () {
const newPosts = this.state.posts.map(el => {
return <Post key={el.id}
title={el.title}
author={el.author}
onClick={this.postClicked(el.id)}/>
})
return
//something
{post}
}
}
}
Now, What is the difference between calling the handler like this
onClick={this.postClicked(el.id)} and onClick={() => this.postClicked(el.id)}
Would appreciate if someone can tell me the difference in general
after Ecmascript 6 javascript was introduced with is arrow function link
here ()==>{//code} is a similar as a function() or anonymous function
tell me if you find out what you want
The first option, "this.postClicked(el.id)", will actually call the method, "this.postClicked", with the "el.id" argument, each time the component renders (probably not what's intended).
The second option, "() => this.postClicked(el.id)", will only call the method, "this.postClicked", with the "el.id" argument, when "Post" is clicked.
Overall, if you can find a way to put the "el.id" argument into an "id" or "name" prop on the component
<Post id={el.id} />
then you can do:
<Post
id={el.id}
onClick={this.postClicked}
/>
this.postClicked = (event) => {
const { id } = event.target;
...
}
This last option avoids the use of an unnamed function. If you use an unnamed function, it will cause unnecessary re-renders. React cannot tell that an unnamed function is the same when it's checking whether or not it should re-render, by considering if the props of a component have changed. It considers the unnamed functions to be a new prop each time it checks, causing an unnecessary re-render each time.
Overall, it won't break your app, but it slows down performance slightly if you do it enough. It comes up especially if you start using React Motion (you'll really notice a difference there). It's best to avoid unnamed functions if possible.
you can read this blog it wil clear the things https://medium.com/#machnicki/handle-events-in-react-with-arrow-functions-ede88184bbb
Differences are,
First method is a wrong implementation and it wont give the intended result, where as second one will work.
In the first method you are making a function call, in second one you are assigning a function's signature to onClick.
It is like the combination of below two statements.
var variableName = function(){//some content};
onClick={variableName}
It looks like you question has already been answered. Just a side note though: remember that when assigning your method with an arrow function
onClick={ () => this.method() }
a new anonymous function is created on every re-render. So if the method doesn't need any arguments, it's better to reference the method directly (without parentheses so it's not invoked).
onClick={ this.method }
The first will call the function every time render is done.
The second will do what you want - call it onClick.

Choosing the correct way to call function in stateful component

So i am having tough time figuring/understanding the correct way to call method inside a class in javascript for example
consider we have stateful component with various method like
addIngredientHandler = (type) => { //Adds one to the state of ingredient }
and
purchasingHandlerOpen = () => this.setState({purchasing: true}) //will show a order summary pop-up if we have more than one ingredient
We pass both of them to child component (using props) by calling them in a return of our stateful component like this
<BuildControls
ingredientAdded={this.addIngredientHandler}
purchasingHandlerOpen={this.purchasingHandlerOpen}
purchasableHandler={this.state.purchasable} />
and In our stateless child component we do
<BuildControl
ingredientAdded={() => props.ingredientAdded(el.type)}
/>))}
<button className={Classes.OrderButton} disabled={!props.purchasableHandler} onClick={props.purchasingHandlerOpen}>Order</button>
</div
Here we have use this at one place
ingredientAdded={() => props.ingredientAdded(el.type)}
and this in another
onClick={props.purchasingHandlerOpen}>
So my question is when do we call a method/function using {() => props.ingredientAdded(el.type)} and when do we use {props.purchasingHandlerOpen} and when do we probably do something like {props.purchasingHandlerOpen()}
Slight Note: In the above example where i do
<button className={Classes.OrderButton} disabled={!props.purchasableHandler} onClick={props.purchasingHandlerOpen}>Order</button>
If I do something like {props.purchasingHandlerOpen()} it throws infinite render error message, I I do something like {() => props.purchasingHandlerOpen} the button does not work.
First of all, you have to understand that the thing you're passing here are just functions, so there is nothing principally different in those 2 ways
There are few points you need to consider though:
First: since react.js uses shallow comparison, every time you're passing
ingredientAdded={() => props.ingredientAdded(el.type)}
you're actually pass function created just now, so it may cause unneeded calls of your children render function (you could easily avoid this by using shouldComponentUpdate though). This could lead to possible performance issues on big react trees so that you second approach is preferred.
Second: you could easily mix a some value via your first approach, something like
ingredientAdded={() => props.ingredientAdded(el.type, SOMETHING_FROM_STATE)}
Third. You can easily modify your event handlers and pass down them in react tree by generating functions which return functions:
class App extends React.Component {
generateFunction(something) {
return (arg) => {
this.props.myFunction(something, arg)
}
}
render() {
return (
<div>
<FirstComponent onClick={this.generateClickFunction('First')} />
<SecondComponent onClick={this.generateClickFunction('Second')} />
</div>
}
}
}
UPD
onClick should always receive function, not its results, like that:
<button ... onClick={props.purchasingHandlerOpen} />
if you are changing onClick to {props.purchasingHandlerOpen()} you are calling the function, so you're passing its result to props.
If you are changing onClick to {() => purchasingHandlerOpen} you are passing undefined (it's not a props.purchasingHandlerOpen, but purchasingHandlerOpen is undefined) so that React considers there is no a callback passed to the props
{() => props.ingredientAdded(el.type)} creates a new funciton which binds the el.type, see arrow functions
{props.purchasingHandlerOpen} does nothing since we do not execute the function (there are no (), call, apply). We simply pass the function reference.
{props.purchasingHandlerOpen()} runs the function.

Passing button's 'this' in react

So to add a function to a button we normally do something like this
<button onClick={this.delete_this.bind(this)}>clickable</button>
But what if I want to pass the button's 'this' as well, so then I can use it to delete itself in a filter function.
For example:
function delete_this(button)
//button_array defined elsewhere
return button_array.filter( (item) => item != button);
How should I go about doing this?
First of all, don't use bind in render, it's not good for performance and it will fail shouldComponentUpdate in most cases. Do it in class constructor instead.
<button onClick={this.delete_this}>clickable</button>
Now you can use:
this to access the component
event.target to access the button
function delete_this(event) {
// event has all kinds of cool stuff, check it out
console.log(event)
// current React component (class)
console.log(this)
}

Categories

Resources