Passing button's 'this' in react - javascript

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)
}

Related

React hooks onclick event with multiple params without unnecessary rerender?

I am using react hooks and functional components and was wondering how I can add multiple params to an react onClick event.
I know there are different options to achieve this. In the past I used this style below (from https://reactjs.org/docs/handling-events.html):
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
But now I am facing this exact described problem from the official react docs. I am getting to many rerenders because of these arrow functions in the onClick attribute:
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.
I have put my function already in a useCallback hook. But if I use this function in a onClick event with an arrow function it will trigger rerenders again.
If I change it to the function reference only it is not triggering rerenders.
So far this is fine.
But: How do I add multiple parameters to this functionreference when using react hooks and functional components.
Will I get by default always the e (event parameter?) as first parameter?
Can somebody explain to me when and how I am getting the react event parameter and when I will not receive this event?
How can I add multiple params beside the event parameter in my onClick attribute?
For example:
What if I have this function and want to use it in the react onClick attribute, prevent unnecessary rerender and add multiple different parameter in the function call
const myClickFunction = (e, value1, value2, value3) => {
// ...
}
// this would trigger rerenders because of the arrow function how do I prevent this?
<button onClick={(e) => myClickFunction(e, "input1", "input2", "input3")}>
Click me
</button>
One trick I like to use in this case is to "bind" the parameters to rendered element using data attributes
const myClickFunction = (e) => {
const value1 = e.currentTarget.getAttribute('data-value1')
const value2 = e.currentTarget.getAttribute('data-value2')
const value2 = e.currentTarget.getAttribute('data-value2')
}
// this would trigger rerenders because of the arrow function how do I prevent this?
<button onClick={myClickFunction} data-value1="a" data-value2="b" data-value3="c">
Click me
</button>
This way you can memoise your function using useCallback safely and you can reuse the same function if you want to pass it to array of children for example. This is not ideal, you couple parents and children and you can only use data which is serializeable to string (basically only primitives).
Better solution would be to store your values somewhere out of component tree so you can access them without closures (for example in redux-thunk you don't need to pass a lot of stuff around, you can just get data you need from store directly by calling getState)

How calling a callback directly fixes the 'this' in a React Class Component?

I would like to learn how is the value of 'this' set when a function called in JSX as a callback to an eventHandler. I noticed that when I call it directly there is no issue accessing state without getting the famous undefined 'this' error, like so:
import React from "react";
class Accordion extends React.Component {
state = { term: "random term" };
onTitleClick() {
console.log("Title is clicked");
console.log(this.state.term);
}
render() {
const renderedItems = this.props.items.map((item) => {
return (
<React.Fragment key={item.title}>
<div className="title active" onClick={this.onTitleClick()}>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className="content active">
<p>{item.content}</p>
</div>
</React.Fragment>
);
});
return <div className="ui styled accordion">{renderedItems}</div>;
}
}
export default Accordion;
When you pass it as just a reference, the famous 'this' is undefined error comes back. Then we know how to bind the 'this' and so on. I feel like I just memorized the solution and now would like to learn the difference.
onClick={this.onTitleClick()} - This is not how you set the event listener. You just need to pass the name of the function instead of calling it yourself.
As far as your question regarding the value of this is concerned, value is set depending on how the function is called. This is not specific to React, this is just how value of this is set in Javascript.
I noticed that when I call it directly there is no issue accessing
state without getting the famous undefined 'this' error
That's because when you call it like this: this.onTitleClick() - onTitleClick() is called on this which refers to the Accordion component. But as mentioned at the start of this answer, this is not how you set the event listener. Instead of calling this method yourself, you need to let javasctipt call it.
When you pass it as just a reference, the famous 'this' is undefined
error comes back
This is the correct way to add an event listener but you get an error because when javascript calls the event handler function, value of this is not your component, i.e. Accordion.
To solve this issue, you have two options:
Explicitly set this using .bind()
this.onTitleClick = this.onTitleClick.bind(this);
Use arrow functions instead of regular functions as event handlers
onTitleClick = () => {
console.log("Title is clicked");
console.log(this.state.term);
}
Following are couple of related questions that might help in understanding this further:
“this” is undefined inside an anonymous function returned by another function React
How does React share methods between classes(components)
When you pass it as a reference, the this is undefined. In order for this to work, you need to bind the function to the class. You can do that in the constructor
constructor(props) {
this.state = { term: "random term" };
this.onTitleClicked = this.onTitleClicked.bind(this);
}
Also, when you pass it to the component, don't call the function, just pass it
<div className="title active" onClick={this.onTitleClick}>
Notice missing parenthesis by the this.onTitleClick call.

React onClick function triggering itself at mount

I have a component that contains a basic onClick function.
The function is called when the component is rendering (without any click), why is it happening?
import React, { Component } from "react";
class Some extends Component {
constructor(props) {
super(props);
}
someFunc = (message) => {
console.log(message)
}
render() {
return (
<p onClick={this.someFunc('hello')}>test</p>
)
}
}
in React you need to pass unexecuted functions, so either
onClick = {this.someFunction}
or if you need to pass an argument
onClick = {() => this.someFunction('argument')}
You don't have to append your function like that. You just need to call it by other ways:
<p onClick={() => this.someFunc('hello')}>test</p>
By using this syntax you can call it by params.
But your first solution was just passing the function
If your function had no argument to pass you could just pass the function like below:
<p onClick={this.someFunc}>test</p>
Because you need to pass some arguments the first one which I mentioned will be your desired one.
You can read their document for further details: React Handling
Events
The reason is because you write
<p onClick={this.someFunc('hello')}>test</p>
instead of
<p onClick={this.someFunc}>test</p>.
If you put there the () the function will be called right on rendering, else only if clicked.
Try this arrow function
<p onClick={() => this.someFunc('hello')}>test</p>
Not tested, but should work correctly this way.
tl;dr: You are invoking this.someFunc('hello') when you render your class, not when you call your onClick property. To fix it, you should use an arrow function, as so:
<p onClick={() => this.someFunc('hello')}>test</p>
Now if you want to know why this happens, let me clarify what you are doing.
import React, { Component } from "react";
class Some extends Component {
constructor(props) {
super(props);
}
// You define a class method that when called will in turn call the
// console.log method, passing message as your argument.
// message will equal 'hello' in your example.
someFunc = (message) => {
console.log(message)
}
// You define a render method of your class, react will
// automatically call this method on render.
render() {
// Your render method (which again is called automatically) will return
// <p onClick={this.someFunc('hello')}>test</p>
// let's see what operations you are performing in this JSX return.
return (
// You first define a paragraph html element to be rendered.
// You then give your element an attribute called onClick.
// But oh no! You are assigning onClick with the value of:
// this.someFunc('hello')
//
// That is not good, as what you are in effect saying is:
// please call the method this.someFunc and pass a single argument: 'hello'
// then assign the return of that method call to my property named onClick.
// Therefore, every time you render this class, you are asking
// javascript to call this function and get its value (which is undefined).
// So while you think onClick is a function, it is not! it is only a value.
// A value which you are asking JavaScript to get for you on each render.
// This is because in JS, when you write functionName(),
// you are literally calling the function at that moment.
// See https://www.w3schools.com/js/js_function_invocation.asp
<p onClick={this.someFunc('hello')}>test</p>
// What you want to do instead is assign your onClick property
// a function! Not a value!
// You want to write, "When I click here, do: this.someFunc('hello')"
// to do that, you have some options, but the end goal will be the same
// What you need to do is assign onClick a value, which when called (onClick)
// will trigger your function! For instance:
// onClick={() => this.someFunc('hello')}
// Above we say "on each render, assign the onClick property a value which IS a function!"
// This is, an unnamed arrow function!
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
// Notice how we have defined an unnamed function that wraps our
// this.someFunc('hello') and we have NOT
// immediately called it (no parentheses following the wrapper function)
// # outer function \() => #inner function \functionName()\ \ <-- No trailing parentheses.
// Therefore, now the only way to execute your method named
// this.someFunc('hello') is by calling its wrapper function.
// And the only way to call that unnamed wrapper function?
// By invoking your onClick property of your p element.
)
}
}

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.

React bind(this, value) in opposite order on function declaration

I have an onClick handler function that passes in the click event and index arguments to the function.
However, turns out I have to declare things in opposite order.. as such:
render(){
return(
<button onClick={this.deleteItem.bind(this, index)}Click Me</button>
)
}
deleteItem(index, e){
e.preventDefault();
this.props.dispatch(deleteTodo(index));
}
This works without issue. But why must this and index be switched in deleteItem()? Is this documented somewhere I am unaware of?
this and index are not being switched. What you're seeing is the difference between using bind to bind a this value and calling the function directly.
In short, if you were to call the function directly, like so:
<button onClick={this.deleteItem}Click Me</button>
You would get the arguments in the order in which you declared them in the function. But, as you obviously know, calling the function in this way leaves you with a this variable referring to the context in which the event handler was called, not the actual class you have declared.
What you're doing instead is calling this.deleteItem.bind, which is necessary to ensure that the this variable is properly bound when it is called in the context of the event.
The better way to do this is the way shown in the React documentation on ES6 classes: bind the event handler in the constructor of the class.
For example,
export class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.initialCount};
this.tick = this.tick.bind(this);
}
tick() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div onClick={this.tick}>
Clicks: {this.state.count}
</div>
);
}
}
By binding the event handler method in the constructor, it is clearer, and you don't have to worry about the context when you use it.
EDIT
Sometimes, you need to pass specific data to your event handler. This is possible using the constructor binding method here. Just do something like this:
constructor(props) {
super(props);
this.myHandler = this.myHandler.bind(this);
}
myHandler(importantValue, event) {
console.log(importantValue); // will log 'pass this value!'
// and you have access to the event object, if you pass it along
// if you don't need the event object, then just leave it out
}
render() {
<div onClick={(event) => { this.myHandler('pass this value!', event); }>
Click Me!
</div>
}
By adding an anonymous function as your event handler in the onClick property for your element, you can do anything you want!
Check the documentation for bind(). bind() fixes/binds the context (this keyword) of the function to the object specfied in the first argument, and the rest of the arguments are prepended to the bound function. That's why index appears before event.

Categories

Resources