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

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

Related

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

What is the difference between func with parentheses and one without it

onHangeChangeQuanity = (opr) => {
let { number } = this.state;
if (opr === '+') {
this.setState({ number: number+1 }, this.onSubmit);
} else if (opr === '-' && number !== 0) {
this.setState({ number: number-1 }, this.onSubmit());
}
}
onSubmit = () => {
let { attributeName, typeOfField } = this.props.attribute;
let { number } = this.state;
let attr = { attributeName, typeOfField, selectedValues: [ number ] };
this.props.onHandle(attr);
}
In the above example in my if condition (opr === '+') I call my onSubmit func without parentheses and it works find and show the current state of my number but in the else condition (opr === '-' && number!==0) I use onSubmit() with parentheses in my setState callback and it doesn't update the state like it should.
Can someone please explain the difference between a javascript function that is being called with and without parentheses.
Thankyou.
Because setState is not guaranteed to be synchronous, it accepts a callback function as the second argument, which it will call after it has updated the component's state.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
Passing the function as a reference (without parentheses) ensures that your onSubmit method will only be called after the component's state has updated and you can rely on this.state to contain the new value.
Calling the function yourself (with parentheses) means that your onSubmit method will always execute before this this.state has the correct value, because it will run before setState does. If this is intentional behaviour it may make more sense to write it explicitly.
this.onSubmit()
this.setState({ number: number-1 });
Without parentheses you only access to it's reference. You can use it to pass the reference for example into another function or use it's properties, because every function is also an object. Look at an example
function first(){
console.log('first');
}
function second(func) { // this get an parameter which is a function
func(); // And here I call it
}
second(first); // Here I pass the function's reference
// And actually call the second with parentheses
With parentheses you execute the body of that function
With the parentheses, you are writing a function call. Without them, you are simply "referring" to the function.
A function call just executes the contents of the function and may or may not return a value. A function reference can be used juts like any other variable. You can pass it to other functions, assign it to another variable etc.
Why are function references useful?
Some functions, like window.setTimeout, take function references as parameters.
window.setTimeout(someFunc, 1000);
Here someFunc will be executed after 1 second. You don't add the parentheses because what you're saying is "I want this function to be called after some delay". Adding parentheses here would have called the function immediately.
Calling a function without parenthesis is referring to the function not what it returns, it is just like assigning and calling it with parenthesis is calling the value of that method, the body of the function executes and may return the required.
When a function name is followed by parenthesis, it is like we say I want to call the function. While when you pass in one or another place a function name, you just pass a copy of the reference to the function.
function sayHello(){
console.log('Hello');
}
// Here we call sayHello();
sayHello();
function executeAnotherFunction(func){
// We request the execution of the function passed.
func();
}
// Here we pass a reference of sayHello to executeAnotherFunction
executeAnotherFunction(sayHello);

invoking curried function without passing another argument

I have a simple function that takes one argument
fn = function(argument) {console.log(argument)}
In setInterval, I want to call the function and pass an external variable:
argument = 1
setInterval(<MY FUNCTION WITH ACCESS TO ARGUMENT>, 1000)
I realize that I could do it with a higher-order function, i.e.
fn = function(argument) {
function () {
console.log(argument)
}
}
argument = 1
setInterval(fn(argument), 1000)
And this does work, but I want to know if it can be done with curry.
I've tried:
fn = _.curry(fn)("foo")
// since the function takes only one argument,
// here it is invoked and no longer can be
// passed as a function to setInterval
fn = _.curry(fn, 2)("foo")
// setting the arity to 2 makes it so the function
// isn't invoked. But setInterval does not pass
// the additional argument and so it never ends
// up getting invoked.
I feel like there is something I'm missing with these curry examples. Am I, or will curry not help here?
Indeed lodash _.curry seems not suitable for your use-case.
But you can use the vanilla JavaScript bind for this:
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.
Syntax
fun.bind(thisArg[, arg1[, arg2[, ...]]])
In your case your code would look like this:
fn = function(argument) {console.log(argument)}
var argument = 1
setInterval(fn.bind(this, argument), 1000)
Lodash alternative
If you really want to do it the lodash way, the equivalant of fn.bind(thisArg, ...args) is _.bind(fn, thisArg, ...args). And if you are not interested in setting the this reference, then you can save one argument with _.partial(fn, ...args):
Creates a function that invokes func with partials prepended to the arguments it receives. This method is like _.bind except it does not alter the this binding.

Why do you have to wrap a callback with an anonymous function?

My html contains two forms overlapping each other, one used as add form and one as edit form. I use jQuery to show and hide them with the following code:
var editForm = $("#edit-form");
var addForm = $("#add-form");
var showEditForm = function() {
editForm.fadeIn(function() {
addForm.fadeOut();
});
};
var showAddForm = function() {
editForm.fadeOut(function() {
addForm.fadeIn();
});
};
I wanted to make the code more compact so I set the fadeOut() call directly on the fadeOut() callback by doing like this:
var showEditForm = function() {
editForm.fadeIn(addForm.fadeOut);
};
var showAddForm = function() {
editForm.fadeOut(addForm.fadeIn);
};
But this productes the following error Uncaught TypeError: Failed to execute 'animate' on 'Element': Valid arities are: [1], but 4 arguments provided. but why doesn't that work?
That's because calling a function as a property of an object is a special syntax, that calls the function with the object as context.
When you call a function like this:
obj.func();
then this will be a reference to obj inside the function.
If you get the reference to the function, and then call it:
var f = obj.func;
f();
then this will be a reference to the global context, i.e. the window object.
By using editForm.fadeIn(addForm.fadeOut); you get the reference to addForm.fadeOut and send to the fadeIn method. It's no longer associated with the object, so it will be called with the global context instead of the object as context.
You can use the proxy method to associate the function with the object, so that it will be called with the correct context:
var showEditForm = function() {
editForm.fadeIn($.proxy(addForm.fadeOut, addForm));
};
var showAddForm = function() {
editForm.fadeOut($.proxy(addForm.fadeIn, addForm));
};
I suspect the problem is that addForm.fadeOut is being called with a bad combination of arguments, when its passed to the fadeIn function (and vice versa).
The classic example of this pitfall seems to be:
["0", "1", "2", "3"].map(function(i) {return parseInt(i);})
This, works as expected and gives [1,2,3,4] as a result. You might expect that you could shorten this, much as you did above, and write
["0", "1", "2", "3"].map(parseInt);
Unfortunately; this evaluates to [0, NaN, NaN, NaN]. The problem, is that .map calls any function provided it with three arguments: the value, the index, and the array itself, and parseInt takes up to two arguments: the value, but also the radix/base to parse in. (e.g. radix 2 to parse a string as binary) So what actually happens is essentially:
[
parseInt("0", 0), //0, radix is ignored
parseInt("1", 1), //NaN, what is base 1?
parseInt("2", 2), //NaN, 2 isn't valid binary
parseInt("3", 3) //NaN, 3 isn't valid ternary/base-3
]
I suspect, based on the error message, that the same thing is going on here. The "arity" of a function is the number of arguments passed to it, so the error message here says that 4 arguments were provided, when only one was expected.
In general with functions that take optional arguments, you need to be careful before passing them to other functions directly, or else you can't control what arguments it will be called with.
Fiddle: http://jsfiddle.net/jmj8tLfm/
addForm.fadeIn and addForm.fadeOut are being called without specifying the this context that would normally be passed when you call addForm.fadeIn(). Try .bind()-ing the this variable appropriately as follows:
var showEditForm = function() {
editForm.fadeIn(addForm.fadeOut.bind(addForm));
};
var showAddForm = function() {
editForm.fadeOut(addForm.fadeIn.bind(addForm));
};
If you are writing in vanilla js. The reason as to why you need too pass the callback function in an anonymous function has to do with function invocation.
Take a look at this example:
const firstFunc = (callback) => {
setTimeout(function() {
console.log('yes');
console.log(callback())
}, 3000);
}
const secondFunc = () => console.log('great');
firstFunc(function(){
secondFunc();
});
// prints 'yes' 'great' after 3 seconds
> yes
great
When invoking the function, if you pass the callback argument without the parenthesis i.e firstFunc(secondFunc); the callback function will invoke after the first function has finished (just like above) provided inside the first function where the callback gets called is invoking that function. i.e callback(), the () is the important part.
Try omitting the parenthesis inside the first function like this, callback and pass the second function as a callback without the parenthesis firstFunction(secondFunction) notice how you are passing the callback function but it is never being invoked. Your console.log() should look like this.
> yes
() => console.log('great')
So why does this matter...
If you pass the function invocation as firstFunc(secondFunc()) using the setup from the first code snippet. You will notice that the second function prints first then 3 seconds later the first function is invoked.
> great
yes
Given that Javascript is event driven, the invocation of the second function can be found const secondFunc = () => console.log('great'); and it will immediately invoke that function not waiting for the response from the first function. That is what the () did when you invoked secondFunc() inside firstFunc.
By passing an anonymous function that function is never being invoked until it reaches the callback() invocation part. Again using the setup from the first code snippet, try.
firstFunc(function(){
secondFunc();
});
firstFunc(function(){
secondFunc();
}())
See how the second call invokes the secondFunc right away. What is happening is the anonymous function is a wrapper to not invoke your function right away.
Why is this useful?
If secondFunc takes a callback that callback function would not be invoked until the second function has finished executing. You would need to call that callback function inside your second function.
firstFunc(function(){
secondFunc(function(){
thirdFunc();
});
});

What's the meaning of "()" in a function call?

Now, I usually call a function (that requires no arguments) with () like this:
myFunction(); //there's empty parens
Except in jQuery calls where I can get away with:
$('#foo').bind('click', myFunction); //no parens
Fine. But recently I saw this comment here on SO:
"Consider using setTimeout(monitor, 100); instead of setTimeout('monitor()', 100);. Eval is evil :)"
Yikes! Are we really eval()-ing a string here? I guess I don't really understand the significance and implications of 'calling' a function. What are the real rules about calling and referring to functions?
In JavaScript functions are first-class objects. That means you can pass functions around as parameters to a function, or treat them as variables in general.
Let's say we are talking about a function hello,
function hello() {
alert('yo');
}
When we simply write
hello
we are referring to the function which doesn't execute it's contents. But when we add the parens () after the function name,
hello()
then we are actually calling the function which will alert "yo" on the screen.
The bind method in jQuery accepts the type of event (string) and a function as its arguments. In your example, you are passing the type - "click" and the actual function as an argument.
Have you seen Inception? Consider this contrived example which might make things clearer. Since functions are first-class objects in JavaScript, we can pass and return a function from within a function. So let's create a function that returns a function when invoked, and the returned function also returns another function when invoked.
function reality() {
return function() {
return function() {
alert('in a Limbo');
}
};
}
Here reality is a function, reality() is a function, and reality()() is a function as well. However reality()()() is not a function, but simply undefined as we are not returning a function (we aren't returning anything) from the innermost function.
So for the reality function example, you could have passed any of the following to jQuery's bind.
$('#foo').bind('click', reality);
$('#foo').bind('click', reality());
$('#foo').bind('click', reality()());
Your jQuery bind example is similar to setTimeout(monitor, 100);, you are passing a reference of a function object as an argument.
Passing a string to the setTimeout/setInterval methods should be avoided for the same reasons you should avoid eval and the Function constructor when it is unnecessary.
The code passed as a string will be evaluated and run in the global execution context, which can give you "scope issues", consider the following example:
// a global function
var f = function () {
alert('global');
};
(function () {
// a local function
var f = function() {
alert('local');
};
setTimeout('f()', 100); // will alert "global"
setTimeout(f, 100); // will alert "local"
})();
The first setTimeout call in the above example, will execute the global f function, because the evaluated code has no access to the local lexical scope of the anonymous function.
If you pass the reference of a function object to the setTimeout method -like in the second setTimeout call- the exact same function you refer in the current scope will be executed.
You are not doing the same thing in your jQuery example as in the second setTimeout example - in your code you are passing the function and binding the click event.
In the first setTimout example, the monitor function is passed in and can be invoked directly, in the second, the sting monitor() is passed in and needs to be evaled.
When passing a function around, you use the function name. When invoking it, you need to use the ().
Eval will invoke what is passed in, so a () is required for a successful function invocation.
First of all, "()" is not part of the function name.
It is syntax used to make function calls.
First, you bind a function to an identifier name by either using a function declaration:
function x() {
return "blah";
}
... or by using a function expression:
var x = function() {
return "blah";
};
Now, whenever you want to run this function, you use the parens:
x();
The setTimeout function accepts both and identifier to a function, or a string as the first argument...
setTimeout(x, 1000);
setTimeout("x()", 1000);
If you supply an identifier, then it will get called as a function.
If you supply an string, than it will be evaluated (executed).
The first method (supplying an identifier) is preferred ...

Categories