I have a LandingPageComponent which has a child component DisplayStudent
I have a function deleteStudent in LandingPageComponent. I'm passing this function as a prop to DisplayStudent but I can't understand why I need to bind function deleteStudent in LandingPageComponent and DisplayStudent as well
I need to get id when delete button is clicked see this on jsFiddle
jsfiddle
import React, {
Component
} from 'react';
import DisplayStudent from './DisplayEmployeeComponent'
var data = [{
name: 'student-1',
id: 1
},
{
name: 'student-2',
id: 2
},
{
name: 'student-3',
id: 3
}
];
export default class LandingPage extends Component {
deleteStudent(e) {
console.log('hi', this, e)
}
render() {
return ( <
div >
<
DisplayStudent studentData = {
data
}
deleteStudent = {
this.deleteStudent.bind(this)
} // BINDING FIRST TIME
/> < /
div >
)
}
}
export default function(props) {
return (
props.studentData.map((ele) => {
return ( <
div key = {
ele.id
}
style = {
{
display: 'flex',
padding: '9px 5px 7px 4px'
}
} >
<
div > {
ele.name
} < /div>
<
button onClick = {
props.deleteStudent.bind(this, ele.id)
} // binding second time
>
Delete <
/button> < /
div >
)
})
)
}
Without .bind(this), this inside deleteStudent points to the global object (window), not to your component.
Learn more about bind and this.
You can avoid first bind even second bind in ES6. Here is working example: https://jsfiddle.net/bayucandra/xch1L072/9/
Explanation:
1st bind
deleteStudent = (e) => {
console.log('hi', this, e)
};
This is a new way of how we do bind(this) for ES6 in Babel. Just syntactic sugar to the old way. The old way is by doing bind(this) in the constructor.
2nd bind:
<button
onClick={props.deleteStudent.bind(this, ele.id)}
> Delete
</button>
You need those bind() because you need to pass ele.id as e argument from : props.deleteStudent(e)
if you don't want to pass it that way, you can use fat arrow of ES6 as follow:
<button
onClick={() => props.deleteStudent(ele.id)}
> Delete alternative
</button>
That way above you will bind function which calls props.deleteStudent(ele.id) inside of it.
You should bind it once.If you do it twice the second one doesn't apply.
It's best practice to implement it on constructor just after super
For more read Arrow functions
It isn't required. props.deleteStudent.bind(this, ele.id) is misleading because this isn't usable inside functional component (it's either a global or undefined) but it doesn't affect deleteStudent because it's already bound to proper context.
If the intention is to bind a callback to proper context, it preferably should be bound once in class constructor:
export default class LandingPage extends Component {
deleteStudent = this.deleteStudent.bind(this); // syntactic sugar for a constructor
deleteStudent(e){
console.log('hi', this, e )
}
render(){
return(
<div>
<DisplayStudent studentData = {data}
deleteStudent = {this.deleteStudent}
/>
</div>
)
}
}
And if deleteStudent should be provided with specific argument instead of the one that will be passed on click event (event object), a cleaner way is wrapper function:
<button onClick={() => props.deleteStudent(ele.id)}>Delete</button
Binding 2 times is not needed at all. Bind is used to pass this reference inside function. So, the first bind inside LandingPage 'DisplayStudent' is enough. Also it is advisible to bind this in constructor, as everytime LandingPage re-renders it is passing a new refrence of function to DisplayStudent. The second bind in onClick is just used to pass parameter to function.
P.S. You can use arrow function to avoid such confusions
Related
I have an event handler which calls a fat arrow function to run a method.
import React, { Component } from 'react';
class App extends Component {
sayHi = msg => {
console.log(msg);
};
render() {
return (
<div>
<button onClick={() => this.sayHi('Hi')}>Console Hi!</button>
</div>
);
}
}
export default App;
I´m learning about contexts and bind() and, I want to convert this example to bind this. My problem is with the parameter that I´m passing when the fat arrow function executes the method, aka, 'Hi'
Is there a way to keep something like this...
<button onClick={this.sayHi('Hi')}>Console Hi!</button>
I tried different ways without good results. Mostly, focused on
constructor(props) {
super(props);
this.sayHi = this.sayHi.bind(this);
}
sayHi = () => {
console.log(msg);
};
And yes... I don´t want to move the 'Hi' to the method or constructor.
I´m trying to learn and understand. I will appreciate any kind of help or orientation.
You are mixing things. There are two cases for your situation and you are trying to use them both.
Binding to this
When do you need you bind your function to this? If you are calling your function in callback like your button (one of the cases of course) and you need to use this in this function then you need to bind it. If you don't use this then there is no need to bind it either.
sayHi() {
console.log("hi");
};
render() {
return (
<div>
<button onClick={this.sayHi}>Console Hi!</button>
</div>
);
}
}
Here, you don't need to bind it, also you can use the function with its reference since there is no argument.
constructor(props) {
super(props);
this.state = {
name: "foo",
}
this.sayHi = this.sayHi.bind(this);
}
sayHi() {
console.log(this.state.name);
};
render() {
return (
<div>
<button onClick={this.sayHi}>Console Hi!</button>
</div>
);
}
}
Here you are using this in the function, so you need to bind it in the constructor or define it as an arrow function.
Your situation
Now, your situation: You are defining your function as an arrow one, no need to bind it anymore if you will use this there. But you are not using it, then no need to use an arrow function. Also, you need to pass an argument to it. So, you need to find a way to accomplish this.
The first method, use an arrow function for onClick. Since if you don't use a callback here you can't use click.
sayHi(msg) {
console.log(msg);
};
render() {
return (
<div>
<button onClick={() => this.sayHi("hi")}>Console Hi!</button>
</div>
);
}
}
If you use like this.sayHi("hi") then this function is invoked in the first render, not with a click.
You can use .bind here as a second method also.
sayHi(msg) {
console.log(msg);
};
render() {
return (
<div>
<button onClick={this.sayHi.bind(null,"hi")}>Console Hi!</button>
</div>
);
}
}
See, we use bind but did not use this since we don't need it. We are not using this in our sayHi function.
I know there are a lot of answers to this situation which involves using bind or arrow function but my situation is a bit different. Basically I want to create a div element with an EventListener that updates the state setIndex value with the index value of the specific child element that I clicked.The callback function for this process is the onModal
If I use arrow function for the onModal, the 'this' keyword gets transferred to the class instead of the individual elements and hence I wont be able to access the index for the individual elements.
If I using the normal function(as I have demonstrated in the code below), the this.setState gets transferred to the individual elements and I get the setState error
class AddRecipe extends Component{
constructor() {
super();
this.state={
name:[],
setIndex:0,
}
}
onLoad() {
var recipe = document.querySelector(".recipe")
const name = data.forEach((d,index) => {
var recipecard = document.createElement("div")
recipecard.setAttribute("class","recipecard")
recipecard.setAttribute("key",index)
recipe.setAttribute("key",0)
var h1 = document.createElement("h1")
h1.setAttribute("id","keyhold")
h1.appendChild(document.createTextNode(data[index].name))
console.log(h1);
recipecard.appendChild(h1)
recipecard.addEventListener("click",this.onModal)
recipe.appendChild(recipecard)
})
}
componentDidMount() {
this.onLoad()
}
onModal() {
var index = this.getAttribute("key")
var recipe = document.querySelector(".recipe")
recipe.setAttribute("key",index)
var modal = document.getElementById('MyModal')
var btn = document.getElementById('myBtn')
var modal = document.getElementById('myModal');
var btn = document.getElementById("myBtn");
modal.style.display = "block";
this.setState({setIndex:index}).bind(this)
}
onClose() {
var modal = document.getElementById('myModal');
var span = document.getElementsByClassName("close")[0]
modal.style.display= "none"
}
render() {
return (
<div>
<div id="myModal" class="modal">
<div class="modal-content">
<span onClick={this.onClose.bind(this)} class="close">×</span>
<ModalWindow datas= {data} index={this.state.setIndex} />
</div>
</div>
<div className ="recipe">
</div>
</div>
);
}
}
export default AddRecipe
Pass the index argument over to the event handler and process accordingly.
Also, since you wanted the element context only for retrieving the index, you can send the index along to the onModal method by using function currying. This way you need not depend on the element's context anymore.
If your React project supports arrow functions for class instance methods then you can try passing the arguments like below.
class AddRecipe extends Component{
...
onLoad() {
var recipe = document.querySelector(".recipe")
const name = data.forEach((d,index) => {
...
recipecard.addEventListener("click",this.onModal(index))
recipe.appendChild(recipecard)
})
}
onModal = (index) => (event) => {
...
this.setState({ setIndex:index })
}
...
}
export default AddRecipe
OR
You can even try the way it's documented in the React Docs to avoid any confusion
https://reactjs.org/docs/handling-events.html#passing-arguments-to-event-handlers
class AddRecipe extends Component{
...
onLoad() {
var recipe = document.querySelector(".recipe")
const name = data.forEach((d,index) => {
...
recipecard.addEventListener("click",(event) => this.onModal(index))
recipe.appendChild(recipecard)
})
}
onModal = (index) => {
...
this.setState({ setIndex:index })
}
...
}
export default AddRecipe
Check out this article to see the many ways of sending parameters to event handlers
https://medium.freecodecamp.org/reactjs-pass-parameters-to-event-handlers-ca1f5c422b9
Probably the error is in the line recipecard.addEventListener("click",this.onModal) you need to add a bind to the this.onModal call.
I think there's no need to use the bind method on setState.
bind will attach the first argument as the context (i.e. 'this") of the function you are calling bind on and return a new function with the new context.
this.setState({...}) is a function call, thus it is not possible to bind any this.
you need something like this:
onLoad(){
var recipe = document.querySelector(".recipe")
const name = data.forEach((d,index) => {
(...)
// !!! bind this to onModal
recipecard.addEventListener("click",this.onModal.bind(this))
(...)
}) }
so if you bind 'this' to onModal, the function called on the click event will use the context of your class and therefore it can call setState of your class.
edit
you can use the second part of Nandu Kalidindis answer, too.
arrow functions automatically have the context of the place where they are created therefore the context of onLoad which is the context of the class.
recipecard.addEventListener("click",this.onModal.bind(this)) onModal have to be bind
and I'm not sure if this will be working without binding
componentDidMount() {
this.onLoad()
}
in constructor add line of code
this.onLoad = this.onLoad.bind(this)
So i'm reading a book on React which said I have to bind my methods like
this.onClickMe = this.onClickMe.bind(this);
but it looks to work just fine without using the above code
class ExplainBindingsComponent extends Component {
onClickMe() {
console.log(this);
}
render() {
return (
<button
onClick={ () => { this.onClickMe() } }
type="button"
>
Click Me
</button>
);
}
}
but it's saying I should do something like this,
class ExplainBindingsComponent extends Component {
constructor() {
super();
this.onClickMe = this.onClickMe.bind(this);
}
onClickMe() {
console.log(this);
}
render() {
return (
<button
onClick={this.onClickMe}
type="button"
>
Click Me
</button>
);
}
}
is this.onClickMe = this.onClickMe.bind(this); still something I have to do? and if so what does it do vs my above example
There are multiple ways to bind your function to the lexical context of the React class,
one such method is to bind it in the constructor,
other method is to use class fields as arrow functions, and
the third way to bind in the render using .bind or arrow,
Each of these can be used, however its best to avoid binding in the render since a new function is returned on each render
Using class field as arrow function.
class ExplainBindingsComponent extends Component {
onClickMe = () => {
console.log(this);
}
render() {
return (
<button
onClick={ this.onClickMe }
type="button"
>
Click Me
</button>
);
}
}
Binding in render
onClick={() => this.onClickMe() }
or
onClick={this.onClick.bind(this)}
is this.onClickMe = this.onClickMe.bind(this); still something I have to do?
You don't have to do it if you use arrow functions that capture lexical this. But it is considered to be a best practice because it allows you to avoid function creation inside render.
render() {
return (
<button
/* creates new function on every render call*/
onClick={ () => { this.onClickMe() } }
type="button"
>
Click Me
</button>
);
}
vs
constructor() {
super();
// creates function once per component instance
this.onClickMe = this.onClickMe.bind(this);
}
In your case, you don't need to because you use arrow function where this is bound to a context in which arrow function is defined - in this case to your component.
this.onClickMe = this.onClickMe.bind(this)
it's necessary when you pass function without any binding so it might be invoked where this will point to another object.
For anyone who is following 'React Step by Step' link and got stuck because in the example Clock, they haven't written any bind and it has worked well, but on the next example Toggle, they started to use bind(this).
Well, you can see the function tick(){...} (Clock class) and handleClick (){...} (Toggle class) are similar, as they both use the word this inside. Well the difference between them is how they are called. In the first (Clock), it is called using arrow function (inside componentDidMount() method) and using it allows you to bind automatically the word this with the object. On the other hand, the second method is not using ()=>{}, and need to bind this with the object. So for this purpose the assignment this.handleClick = this.handleClick.bind(this); helps you.
There are 3 ways to bind this
While defining the state. Like this:
this.state = {
this.eventHandler = this.eventHandler.bind(this)
}
Change the normal function to arrow function. Like this:
eventHandler = () => {
console.log('event handler');
}
Pass arrow function directly into the props. Like this:
<input onClick={(e) => this.eventHandler(e) } />
Hope this could resolve your problem.
~RDaksh
I'm aware of JavaScript's scopes but probably I don't understand them fully because this code doesn't work.
This code uses React and Relay Modern frameworks.
There are 2 buttons, first one inside queryRender which is passed into Relay Modern QueryRenderer and second one afterwards (see function render). The second one is working, first one doesn't execute the clickTest function.
(This is simplified version of actual code)
class Candidates extends Component {
static propTypes = {
viewer: PropTypes.object
}
constructor (props) {
super(props)
this.clickTest = this.clickTest.bind(this)
}
clickTest () {
console.log('click works')
}
queryRender ({error, props}) {
if (error) {
return <pre>{error.message}</pre>
} else if (props) {
return (
<div>
<Button onClick={this.clickTest}>this DOESN'T work</Button>
</div>
)
}
return <Loader active>Loading...</Loader>
}
render () {
return (
<div>
<QueryRenderer
environment={environment}
query={query}
render={this.queryRender}
/>
<Button onClick={this.clickTest}>this works</Button>
</div>
)
}
}
The query variable is defined, I just didn't include it in that excerpt.
When I substitue first button's onClick function with an anonymous one
<Button onClick={() => this.clickTest()}>this DOESN'T work</Button>
then I get such error: Uncaught TypeError: _this2.clickTest is not a function
Can anyone explain to me why this code behaves the way it does?
In javascript, the meaning of this isn't determined when a function is created, but rather when it is invoked. When QueryRenderer invokes your queryRender function, it doesn't know that it needs to invoke it in the context of your class, so this will not be referring to what you think it's referring to.
You'll either need to bind your queryRender function, much like you're doing with your clicktest function in the constructor, or you'll need to redesign queryRender so it doesn't need a reference to this.
To expand upon both Artur and Nicholas' answers, you either need to bind() this or use an arrow function to make sure that this is referring to the component itself. You already have the bind method down, here's en example of the arrow function which gets rid of the need to bind because arrow functions don't actually bind a this value, they use their parents scope instead...
class Candidates extends Component {
static propTypes = {
viewer: PropTypes.object
}
constructor (props) {
super(props)
this.clickTest = this.clickTest.bind(this)
}
clickTest () {
console.log('click works')
}
queryRender = ({error, props}) => {
if (error) {
return <pre>{error.message}</pre>
} else if (props) {
return (
<div>
<Button onClick={this.clickTest}>this DOESN'T work</Button>
</div>
)
}
return <Loader active>Loading...</Loader>
}
render () {
return (
<div>
<QueryRenderer
environment={environment}
query={query}
render={this.queryRender}
/>
<Button onClick={this.clickTest}>this works</Button>
</div>
)
}
}
Arrow function doesn't create new scope and its scope is enclosing execution context, in this case it's QueryRenderer scope where you don't have this function. When you pass it as simple function then the scope will be undefined or not, I don't know what Button does inside. I haven't used Rely and not sure you can refer to component from Rely render method.
I would like to pass the parent div id, on click of that div or any child element of the same div. But I am unable to achieve it. Please tell me where I am making a mistake. Code is below:
viewMore: function(i,j){
console.log('You clicked: ', i );
},
render : function(){
var attributeId = "groups_";
attributeId+= index;
return(
//parent div
<div className="groups" id={attributeId} onClick={this.viewMore}>
<div className="floatLeft"> Group Name: <h3>My Name</h3></div>
<span className="floatRight typeCd">POC</span>
<div className="clearfix"> Key Attributes:
<ul>
<li> POC 1</li>
</ul>
</div>
</div>
)
};
viewMore = (i,j) => () => {
console.log(i,j)
}
To pass parameters to event handlers we need to use currying.
With the above method no new functions created all the time while render is called.
Since I see these kind of suggestions in multiple places, I am going to move my comment into an answer as well, to provide an additional view:
class TestComponent extends React.Component {
constructor() {
super();
this.onClick = this.handleClick.bind(this);
}
handleClick(event) {
const {id} = event.target;
console.log(id);
}
render() {
return (
<div>
<h3 id={this.props.id} onClick={this.onClick}>
{this.props.name}
</h3>
</div>
);
}
}
This allows to:
avoid unnecessary binds
access the id and whatever else properties in a much more React-ive manner.
Of course, the above example assumes that you receive the id as a prop, but you can do the necessary manipulations as well.
UPDATE 1 -- Nov 28, 2016
Added link to CodePen from comments above.
UPDATE 2 -- Mar 30, 2017
As mentioned, this wouldn't work if you use React.createClass to define your components. You don't have a constructor to pull this off. You can use other lifecycle methods, if you don't mind a little ugliness.
Having said that, it is 2017. Use ES6, would you?!
UPDATE 3 -- May 12, 2017
If you are using class properties transform, then you can simplify it further:
class TestComponent extends React.Component {
onClick = (event) => {
const {id} = event.target;
console.log(id);
}
render() {
return (
<div>
<h3 id={this.props.id} onClick={this.onClick}>
{this.props.name}
</h3>
</div>
);
}
}
UPDATE 4 -- Feb 4, 2018
Due to improvements of bind and friends in V8 (Chakra and such probably too), you just may be better off using the this.click.bind(this) or wrapping it in an arrow function when passing to onClick.
Why?
The previous method, created for performance reasons only, closed some possibilities for dynamically injecting functions onto the component's prototype.
NOTE 1 -- Apr 14, 2018
Keep in mind that the method mentioned in Update 4 still introduces some performance issues, as on each render pass a new function is created as a result of bind. This, in turn, will trickle down to the child component and cause unnecessary re-renders, as the function changes each time.
The same thing happens when you pass an arrow function inline.
All other methods, like using class properties, will mess with your inheritance (which you should be avoiding, but still), simply due to the fact that, currently, Babel transpiles them to "on-instance" functions, which are not on the prototype chain.
So, this:
class Person {
printA = () => { console.log('a') }
}
becomes:
function _classCallCheck(instance, Constructor) {...abridged...}
var Person = function Person() {
_classCallCheck(this, Person);
this.printA = function () {
console.log('a');
};
};
I've made an updated answer for ES6 here:
https://stackoverflow.com/a/35748912/76840
Essentially, you can use arrow function expressions, which have the benefit of preserving this:
onClick={(event)=>this.viewMore(attributeId, event)}
As of this edit, if you're using Babel with stage-2 enabled, you can use a property like so:
// Within your class...
viewMore = (event) => { /* ... */ }
// Within render method in your JSX
onClick = {this.viewMore}
You can use currying function.
ES5:
viewMore(param) { // param is the argument you passed to the function
return function(e) { // e is the event object that returned
};
}
ES6
viewMore = param => e => {
// param is the argument you passed to the function
// e is the event object that returned
};
And just use it like this:
onClick={this.viewMore("some param")}
Here is an update and an overview of previous answers:
Using onClick={this.viewMore.bind(this, attributeId)} by #HenrikAndersson
.While this approach serves the purpose it uses the bind syntax with which many are not comfortable.
Using public class field mentioned by #ZenMaster.This solution has more or less the same performance, it also comes with a better syntax. But it turns tricky when we have to pass a parameter.
class TestComponent extends React.Component {
onClick = (event) => {
const {id} = event.target;
console.log(id);
}
render() {
return (
<div>
<h3 id={this.props.id} onClick={this.onClick}>
{this.props.name}
</h3>
</div>
);
}
}
The above mentioned approach skips passing parameters and instead uses custom attributes to access the data required in click handler.
A better solution would be :
class MyComponent extends React.Component {
handleClick = (item) => (e) => {
e.preventDefault()
console.log(`This has access to item ${item}! and event(e)`)
}
render(){
const item={ id:'1', value: 'a' }
return(
<button onClick={ this.handleClick(item) } >Click</button>
)
}
}
Reference: Handle events by arrow functions in React app
Here is the code how to use on click event.
var attributeId = "groups_";
attributeId = 32;
const viewMore = (val) => () => {
console.log(val)
}
return (
//parent div
<div className="groups" id={attributeId} onClick={viewMore(attributeId)}>
<div className="floatLeft"> Group Name: <h3>My Name</h3></div>
<span className="floatRight typeCd">POC</span>
<div className="clearfix"> Key Attributes:
<ul>
<li> POC 1</li>
</ul>
</div>
</div>
)