React - Arrow function vs bind() and event with argument - javascript

I'm a newbie in react (even in JS). I'm made the following code in my App class:
nameChangeHandler = (event, personId) => {
//method code
};
render()
{
<div>
{this.state.persons.map((person, index) => {
return <Person
// nameChangeHandler={(event) => this.nameChangeHandler(event, person.id)}
nameChangeHandler={this.nameChangeHandler.bind(this, person.id)}
/>
})}
</div>
}
I'm passing nameChangeHandler to Person component where I call it in onChange event in input tag (<input type="text" onChange={props.nameChangeHandler}/>)
Application works fine when I pass it in this way:
nameChangeHandler={(event) => this.nameChangeHandler(event, person.id)}
but it doesn't when I do it like this:
nameChangeHandler={this.nameChangeHandler.bind(this, person.id)}
It fails with an exception when I'm trying to acces event.target.value in nameChangeHandler method.
How can I pass event and argument to this method using bind() method instead of arrow function?
I've heard we should always use bind() approach over the arrow function because an arrow function approach could render component many times.
Is there any special use cases and differences between bind() and arrows functions?

It's a matter of timing.
When you use bind, the arguments map on to the function you are binding in the order you bind them followed by any that are passed to the new function.
When you use an arrow function, you are explicitly passing them in the order you determine.
Your function expects event to be the first argument, so when you use an arrow you function you pass event, person.id. However, when you bind you pass person.id (the first argument) and when the function is called with event you pass that (as the second argument).
This means you arguments end up the wrong way around.
Since you only have person.id when you call bind and you don't get event until later, the only way you can use bind for this is to change the original function so it accepts the arguments in a different order.
nameChangeHandler = (personId, event) => {

Related

Difference in handling onClicks with functions REACT

What is the difference between these two lines:
<button onClick={() => this.updateTodoToShow("all")}>all</button>
and
<button onClick = {this.deleteCompleted}>delete completed</button>
Why is one called with an arrow function and not the other? When is it appropriate to do each of these? Here is the full code for those two functions:
deleteCompleted = () => {
this.setState({
todos: this.state.todos.filter(todo => !todo.complete)
});
};
updateTodoToShow = s => {
this.setState({
todoToShow: s
});
};
The first uses an anonymous arrow function in order to pass a value other than the event to the function updateTodoToShow.
The second apparently does not need to pass anything besides the click event which is the default argument (in fact, it doesn't appear to use the event either), so it only passes the function reference to this.deleteCompleted.
If no extra parameter is needed for the event handler, passing only the function reference is better. Creating an unnecessary inline function is pointless, like in the following:
onClick={(e) => this.handler(e)}
Is functionally equivalent to below, there is just no unnecessary in-between function:
onClick={this.handler}
The main thing to understand here is that both methods are passing onClick a function reference. The first is passing a reference to the inline function, the second is passing a reference to the deleteCompleted function.
It is appropriate to use the inline function only when you need to add extra arguments to the handler. Another common usecase would be passing an id or other identifier when creating buttons in a loop.

React: Why <Input> puts the event object at the end of the argument of the event handler

The title may sound vague, so here is a snippet of code to explain what it means...
changeInput = (index, event) => {
//How come "event" is the second argument?
//I didn't put "event" (but index) in the code below.
//Why isn't the argument (event, index)?
}
render() {
const index = 0;
return (
<input type="text"
onChange={this.changeInput.bind(this,index)} />
);
}
}
In React.js, form elements (input, textarea etc) send the event object to the event handler (changeInput).
However, I'm a little confused with the order. Why event is put at the end of the event handler, instead of at the beginning?
It might be nicer to use an anonymous function for onChange.
(i.e. (event) => this.changeInput.bind(event,index) may be better looking)
Before doing so, I'd like to know the reason, but can't find any clue in the official doc.
Any advice will be appreciated.
It's because of index here .bind(this, index). Bind passes first arg as a context and all other args will be passed to the function on call
Here is an implementation of bind for better understanding:
Function.prototype.bind = function bind(context, ...bindArgs) {
return (...args) => {
return this.call(context, ...bindArgs, ...args);
};
};
Check the documentation for bind. The first arg you send to bind is thisArg (which is used to determine this inside the function, changeInput in your case), the rest arguments is passed to the function.
this.changeInput.bind(this, arg1, arg2, arg3)
Will result in
this.changeInput(arg1, arg2, arg3)
The reason you've to call .bind is a story itself. You can search for "javascript this context" to learn more about this and how it works, it's worth learning that :)

when to use function() , function or () => function(callback)

I have been searching for a while for a good explanation so its all clear to me.
Example:
<Char click={()=>this.onDeleteHandler(index)}/>
vs
<Char click={this.onDeleteHandler()}/>
vs
<Person changed={(event) => this.nameChangedhandler(event, person.id)} />
and
<Char click={this.onDeleteHandler}/>
regarding the third code , here is the property called:
nameChangedhandler = (event, id) => {
const personIndex = this.state.persons.findIndex(p => {
return p.id === id;
});
// copying the person with the right index
const person = {
...this.state.persons[personIndex]
};
// Assigning new name to this person
person.name = event.target.value;
// copying array of persons, then manipulating the correct object of person by using the index
const persons = [...this.state.persons];
persons[personIndex]= person;
this.setState({
persons: persons
});
}
some aspects are clear to me, but definitely not 100%!
So if you can provide me with an explanation, link or similar that would be great!
thanks!
<Char click={()=>this.onDeleteHandler(index)}/>
It passes anonymous function as a callback which - when clicked - triggers onDeleteHandler with extra index parameter (which has to be defined in the scope before).
<Char click={this.onDeleteHandler()}/>
It passes result of onDeleteHandler() as a callback - probably a bad idea - onDeleteHandler function has to return another function that will be invoked on click.
<Person click={changed={(event) => this.nameChangedhandler(event, person.id)} />
Looks invalid - will result with syntax error.
<Char click={this.onDeleteHandler}/>
Similar to the first example but doesn't take custom parameters. Default click event will be passed as a first argument for onDeleteHandler
The whole question seems to boil down to what the difference between func and func() and () => func() is. It has nothing to do specifically with React.
If I have a function
function func() {
console.log(42);
}
Then I can reference the function object itself via func. This is useful if I need to pass the function to another function, e.g. setTimeout:
setTimeout(func, 1000); // calls func after 1000ms
setTimeout expects a function that it can call after the provided timeout.
Similarly in React, click, change etc are all props that expect to be passed a function that is called when the event happens.
func() on the other hand calls the function. This needs to be done if you actually need to call function right then and there.
This implies that if I do
setTimeout(func(), 1000);
then I would call func first and pass its return value to setTimeout (i.e. I call func now, setTimeout doesn't call it later). So this is usually incorrect unless func returns a function itself and its really the return value I want to pass to the other function.
() => func() is just a new function that only calls func. For all intends and purposes it is equivalent to func:
function func() {
console.log(42);
}
const anotherFunc = () => func();
func();
anotherFunc();
And of course if func expects an argument then I have to pass it along when wrapping it in another function, which is what x => func(x) does.
The other part is how functions assigned to object properties and this work. In short, what this refers to inside a (non-arrow) function depends on how the function is called. Doing
this.foo();
// or
var bar = this.foo;
bar();
produces two different results because this.foo() and bar() are two different ways to call the function. For more info see How to access the correct `this` inside a callback?
Generally you would make use of inline arrow functions when you need to bind he handler to the context or to provide custom parameters
In
<Char click={()=>this.onDeleteHandler(index)}/>
onDeleteHandler is bound to the context where Char is rendered and is passed a custom parameter index. Since a new function is returned to click , it can be executed from within Char like this.props.click()
<Char click={this.onDeleteHandler()}/>
Here the onDeleteHandler is evaluated and the value is returned to the click prop
<Person click={changed={(event) => this.nameChangedhandler(event, person.id)} />
Here the syntax is invalid, it should probably be
<Person changed={(event) => this.nameChangedhandler(event, person.id)} />
In which case, it takes the default parameter and pass it along with the custom parameter to nameChangedHandler and it also performs binding
<Char click={this.onDeleteHandler}/>
just assigns the reference of onDeleteHandler to click and whenever you invoke click, onDeleteHandler will be called with the parameters that you pass while invoking click and the context within onDeleteHandler will refer to the context from where it is invoked unless you bind onDeleteHandler using arrow function or in constructor

Passing param to a method in event listener without adding anonymous function

I have an element on which I add 'input' event listener and assign a function to be executed during an input on an element. That function takes an argument, and I want to pass the same element to the function as an argument without adding an anonymous function to the event listener.
My event listener:
$(element).on('input', testMethod);
My test method:
function testMethod(paramElement)
{
// Do something
}
I wish to pass the element as the param element to testMethod during input on the element. Presently, when I input anything on the element, my paramElement is a jquery event and the element is within event.target. I wish my param element to be directly a javascript element and not a jquery event.
For this I tried adding an anonymous function in the event listener (as below), which works, but I am trying to figure out if I can pass the param without adding a anonymous function.
Event listener with anonymous function:
$(element).on('input', function(){testMethod(event.target);});
Is there a way to pass the param/element without using an additional function just to pass a param?
You can add a custom value to the arguments passed to a function by using the .bind() method [MDN]:
function handleKeys(arg, e) {
console.log(arg, e);
}
var input = document.querySelector('input');
input.addEventListener('keydown', handleKeys.bind(null, 'cats'))
Codepen
From the MDN documentation:
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
bind() has he following function signature:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
In other words, we call bind() on a function (fun). The first argument passed to .bind() determines the value of the this keyword inside fun. Any additional arguments are passed to fun. Finally, any additional arguments that would be provided to fun are provided after these arguments from .bind.
So in the example above, we bind a null context to handleKeys then pass the string cats, and the handleKeys() function receives cats as its first argument followed by the event object that is automatically passed to event callbacks...

How to use bind in React?

I'm using React, and I saw that one common practice is to bind the functions in the constructor, which I also want to use. Though, I don't exactly get how bind works for functions that take parameters. For example, I have something like this:
class MyClass extends Component {
constructor(props) {
super(props);
this.onListClicked = this.onListClicked.bind(this);
}
onListClicked(id) {
// performs some action
}
render() {
return (
// partially removed for brevity, value of myId comes from a loop
<ListItem onClick={ () => this.onListClicked(myId) } />
);
}
}
Now this works in my case, but I'm not taking advantage of bind fully. If I change the ListItem to <ListItem onClick={this.onListClicked} /> it doesn't work as expected. This would have worked if onListClicked didn't accept any parameters. But, in this case I don't know how to take advantage of bind. Any ideas?
your question has little to do with binding, and is really about how React handles callback props.
Every React event-listener function is passed an instance of React's SyntheticEvent object as its first parameter.
onClick={this.onListClicked} will call the onListClicked function and pass it one parameter: the SyntheticEvent object provided by React.
onClick={this.onListClicked.bind(this)} is the same as the last example. onListClicked.bind() returns a wrapped version of onListClicked with its context object set to this (which in your case is your React component because that's what this is set to when you do the binding). This wrapped version of your function still only receives one parameter: a SyntheticEvent object.
onClick={(e) => this.onListClicked(myId)} will call the anonymous fat-arrow function and pass it one parameter: a SyntheticEvent object, because the anonymous fat-arrow function is the callback and all callbacks get that parameter. This anonymous fat-arrow function ignores its own parameters, and calls this.onListClicked with the value of myId.
onClick={() => this.onListClicked(myId)} is the same as the last example, except we are ignoring the SyntheticEvent because we don't care about it.
onClick={this.onListClicked.bind(this, myId)}, as suggested in another answer, will wrap and call the onListClicked function and pass it TWO parameters: the first is myId (since bind is injecting myId as a parameter as well as setting the context to this) and the second is a SyntheticEvent object.
So: depending on what exactly you are doing inside of onListClicked, you may or may not need to bind it to your React component (or to some other context). Do you actually need variables and functions defined inside a particular object? Then bind your callback context to that object, and call this.foo or this.bar all you need. But if you don't need access to those sorts of things, there's no need to use bind just because it's there.
bind function takes a context as its first argument and takes your original function arguments as the next set of arguments after this. The "binded" (excuse me for the horrendous grammar!) function that is returned has the same arguments "binded" to it so when you call it, it will be called with the same set of arguments that it was bound with.
So essentially a <ListItem onClick={ () => this.onListClicked(myId) } /> should be replaced by a <ListItem onClick={this.onListClicked.bind(this, myId)} />
I, however, don't see a way for you to generate the function you need in the constructor itself since you won't have those arguments there to start with. You could loop over the array in the constructor itself and create those "binded" functions there only but that would be just a waste and a much elegant solution would be to use the above said method.
Read more about bind from MDN.

Categories