Binding this on method receiving parameters - javascript

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.

Related

pass parameters to a javascript function without using an inline function inside jsx

So after reading a few articles on react native performance, I realized that you're supposed to avoid arrow functions inside JSX.
https://blog.codemagic.io/improve-react-native-app-performance/#avoid-arrow-functions
But I have the following react-native code
function Layout() {
function handlePress(index) {
console.log("Inside HandlePress", index)
// I want index here
}
function NavItemIterable(item, index) {
return(
// how to pass index without arrow functions
<Pressable key={index} onPress={handlePress}>
<NavItem />
</Pressable>
)
}
return(
<Box>
{data.map(NavItemIterable)}
</Box>
)
}
With arrow functions, I could do something like
<Pressable key={index} onPress={()=> handlePress(index)}>.
How can I avoid an arrow function is this case & still call handlePress with index.
(or should I even try to avoid)
That blog post you have mentioned is at least misleading, and doesn't give sufficient context to understand it's statement (e.g. why you would even use a nested function in that case at all).
Your example (a function component) is fundamentally different to the one in that blog post (a class component).
In your example, it is ok to use a function to "pass" the parameters to the handler.
That is generally inline with common react principles, and it doesn't matter a lot if you are using
an arrow function (const f = () => {};) or a classic function (const f = function(){};). (optimizations are still possible.)
What the blog post says, and doesn't say ...
The blog post states that you should "Avoid Arrow Functions", and gives an example.
In that example there is a difference between using a classic and an arrow function, but that isn't the actual problem,
and the blog post doesn't say why you would use an extra function there at all (no matter if classic or arrow function) ?
class MyClass extends React.Component {
addTodo() {}
render() {
return <>
<MyComponent onClick={ () => this.addTodo() /* <-- Why would you even do this ? ... */ } />
<MyComponent onClick={ function(){ this.addTodo() } /* <-- ... even with a classic function ? */ } />
<MyComponent onClick={ this.addTodo } /> /* <-- Recommended. */
</>;
}
}
Of course, you should avoid that extra function, if there is no reason to add it.
What the blog post probably means ...
A common reason to use an extra function there is to capture the this context of the class. Indeed, here you need to use an arrow function.
In this example, only the arrow function will work (and the alternative), because it captures the this context of the component:
class MyClass extends React.Component {
myValue = 'some value';
addTodo(){
try { console.log( 'ok, value is:', this.myValue ); }
catch(_){
console.error( 'error: `this` is:', this );
}
}
render() {
const capturedThis = this;
return <>
<button onClick={ () => this.addTodo() } > arrow: works. </button>
<button onClick={ this.addTodo } > no extra function: error. </button>
<button onClick={ function(){ this.addTodo(); } } > classic function: worse error! </button>
<button onClick={ function(){ capturedThis.addTodo(); } } > classic alternative: works. </button>
</>;
}
}
What the blog post missed ...
The captured this context ("closure") might cause problems. That is probably why the blog post recommends to avoid it.
But the actual recommendation should not be to "avoid arrow functions", but to avoid capturing the this context in a closure (which is usually done by using an arrow function).
A solution would be to bind the method to the component:
class MyClass extends React.Component {
constructor(props){
super(props);
this.addTodo = this.addTodo.bind(this); // <-- bind method to the components `this`
}
myValue = 'some value';
addTodo(){
console.log( 'ok, value is:', this.myValue );
}
render() {
return <>
<button onClick={ this.addTodo } > works without arrow function </button>
</>;
}
}
Remark:
I would recommend that you just should forget that you have ever read the misleading recommendation in that blog post, and find better sources of knowledge.
I think closures, the .bind(this) pattern and optimizing functional components are beyond the scope of this question topic.
If you still want to optimize your functional component, unrelated to that blog post, then I think you should ask a new question.

Why twice bind on onClick is required in React?

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

React binding this to a class method

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

What is the reason that function is not passed to a Button

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.

want to execute disNone() function inside saveInfo() function when click on button

I want to execute disNone() inside saveInfo() when the button is clicked:
Error:TypeError: Cannot read property 'disNone' of undefined
import React, { Component } from 'react';
class Login extends Component {
constructor(props){
super(props);
this.state = {
dispNone:"dispNone",
message:"dispNone"
};
this.disNone = this.disNone.bind(this);
};
disNone(){
this.setState({
dispNone: "dispNone",
message:"dispBlock"
});
}
saveInfo(){
this.disNone();
}
render() {
return (
<div>
// other code
<button onClick={this.saveInfo}>Sign up</button>
</div>
);
}
}
export default Login;
In the constructor, besides this.disNone = this.disNone.bind(this), you also need to put
this.saveInfo = this.saveInfo.bind(this);
the error is because safeInfo does not know what this means, which gives you the error that disNone is undefined
EDIT: We do this in the constructor because we only need to bind the function once. Alternatively you could write it in the render function as well, but that means everytime the render function executes, you rebind the the function which is kind of a waste.
A third alternative is to use the () => this.saveInfo() inside the render function, this does not require any kind of binding anywhere, but again, this function has to be "created" every time the render function runs.
add
this.saveInfo = this.saveInfo.bind(this);
in your constructor.
SaveInfo method is not having access to this.

Categories