'm learning native react and learning more about javascript, so I still do not understand many things about its behavior. I have created a button component, using TouchableOpacity, and its props onPress.
For it to work, I must send the function I want to execute as a props, but here I have fallen due to my little experience.
I am sending a console.log that is activated when I press the button. So I passed the console.log directly in the props that I defined, but it did not work when I clicked on the button, but, it was executed when the code is initialized. On the other hand, when I have passed an arrow function that contains the console.log, it has been executed at the moment of clicking the button.
to make it clearer I'll show my code:
this is my button component:
const Button = ({ onUserPress, children, color }) => {
const state = {
};
return (
<TouchableOpacity onPress={ onUserPress } style={styles.buttonStyle} >
<Text style={styles.textStyle}>
{children}
</Text>
</TouchableOpacity>
);
};
and i call them like this:
<Button onUserPress={ console.log("hello") }>Something</Button>
This is executed at the time of initializing my application, and nothing is happened when the button is pressed.
on the other hand, when i use the same component, but i pass the console.log in arrow function, like this:
<Button onUserPress={ ()=>{console.log("hello")} }>Something</Button>
this execute the console log only when i press the button component.
Could someone explain to me why the behavior is different? The world of functional programming is totally new to me and these things seem strange to me. According to what I understand, both are functions, so for me they do not make a difference (obviously, from my ignorant point of view). What is the difference that makes the behavior different?
You aren't comparing a "normal function" with an arrow function.
The content between { and } is an expression which is evaluated and the result is assigned to the prop.
Thus you are calling console.log("hello"), taking the return value and assigning that as a function to onUserPress (this makes no sense as the return value of console.log is not a function).
Not a function:
console.log("hello");
If you don't want use arrow function you need to pass a regular function: onUserPress={ function(){console.log("hello")}.
Also, you can pass onUserPress={console.log}, in this case, console.log will receive an event object as a parameter.
Difference in function(){console.log("hello") and () => console.log("hello") that in second case "this" will be pointed to your component
1-> <Button onUserPress={ console.log("hello") }>Something</Button>
2-> <Button onUserPress={ () => console.log("hello") }>Something</Button>
in first : you're calling the console.log("hello") ,
where as in 2nd you're returning console.log("hello")
so its similar to
const functionCall = ()=>{
do something...
}
const x = document.getElementById("root")
x.addEventListener("click",functionCall()) <--- WRONG!!!
in the above case it will CALL the function,even before you press the
input/button which is "called" inside the 2nd parameter.
Rather
x.addEventListener("click",functionCall) <---- Correct it executes
the function when you click the button/input
Related
this is an exercise from codecademy. Why does it require to pass the event handler as a reference? why i can't type onClick={goBack()} instead of onClick={goBack}
const goBack = () => {
setQuestionIndex(prevQuestionIndex => prevQuestionIndex - 1);
}
....
<button onClick={goBack}>
Go Back
</button>
When you are using onClick={goBack()} then you are invoking/calling the function goBack and the result of goBack invocation get passed to onClick
Live Demo
But If you are using as onClick={goBack}, then In this case you are passing a reference to the onClick which means when user click on the button then It will automatically invoke the function. You don't have to invoke the function yourself.
1) Let say consider function with value
<button onClick={fun()}> button with function reference</button>
If you are passing value to a function then first of all it will produces a warning in the console as:
Warning:
Expected `onClick` listener to be a function, instead got a value of `string` type.
and when you invoke it then it will throw error as
Error
Expected `onClick` listener to be a function, instead got a value of `string` type.
2) If you are using as
<button onClick={fun}> button with function reference</button>
then function fun will be passed as a prop and when use click on the function then this function fun will get invoked automatically
goBack is a reference to a function, goBack() calls that function and returns the result.
In the case of the click handler for a button, of course, you want the function to run when the button is clicked, not when the button is rendered. That's why you need to pass the function, rather than calling it (on render) and passing its result.
If you pass in the function call instead of the reference, it will call the function as soon as your component renders, the reference is bound to the click handler, hence when the specific event triggers, it will callout to the reference.
Here is a snippet to see this,
Sandbox
In a tutorial related to React, which I am watching on YouTube:
https://www.youtube.com/watch?v=h3KeJRKYpj8
there was a piece of code that had the following (minute 13:40 in the video)
const setCounterWithValue = (value) => {
setCounter(value)
}
this function gets called from a button component in the following code:
<button onClick={() => setCounterWithValue(counter -1)}
The video tutorial suggested a way to shorten this function call and make it simpler.
So what he did was modifying the code to be as follows (minute 14:30 in the video):
const setCounterWithValue = value => () => {
setCounter(value)
}
which meant the button component became as follows:
<button onClick={setCounterWithValue(counter -1)}
So my problem here is that I was expecting the setCounterWithValue to be structured as the following:
const setCounterWithValue = () => (value) => {
setCounter(value)
}
so empty () then pass value which triggers the setCounter.
I would like to have some deep explanation and some resources (like articles and videos) that explain this in further details.
I disagree that its simpler... What they are doing is called currying. Its a shorthand way to create a function that returns a function. So a longhand form would look like this:
const setCounterWithValue = (value) => {
return function () {
setCounter(value)
}
}
Its probably easier to understand why the parameters go where they do when written in this form. When you call it inline you're calling the outer function <button onClick={setCounterWithValue(counter -1)} />. It then returns the a function that should be called when the button is clicked.
What they're doing there is called currying. In other words, it's a function that returns another function. Instead of firing it outright, they're taking the value of the first function's argument and utilizing it in the second function that is fired from the the click event. In this particular case I don't see it being any simpler as you're getting the same exact outcome but in this case your original function is just fired in the second function body. Nevertheless, more info on currying can be found here.
Calling the "simpler" function with setCounterWithValue(counter - 1) will return a function that, when executed, will pass through the counter value provided. The concept for this is called currying through the use of a closure. The function you "expected" wouldn't make much sense because you'd be calling it with a value, but that value never gets captured, so the function it returns is expecting a value parameter, which is not passed, it gets lost.
Here's the two examples:
Example 1:
const setCounterWithValue = value => () => {
setCounter(value)
}
When called with setCounterWithValue(42), it will return the following:
() => { setCounter(42) }
Example 2:
As opposed to what you said you expected, which would end up like the following:
const setCounterWithValue = () => (value) => {
setCounter(value)
}
Called with setCounterWithValue(42) would return the following:
(value) => { setCounter(value) }
Notice how the function being returned still expects a value to be provided. This is the difference between the two examples and why Example 1 would work, while this one wouldn't.
This is an example of currying, which you can use as an introduction to Higher Order Functions which are very common in the React ecosystem.
To break down what happens in this case and why your assumption on how the function should look is wrong, you first have to understand what JavaScript closures are.
Let's say we have this piece of code:
function foo() {
var bar = 5;
return function baz() {
console.log(bar);
}
}
foo(); // Returns a function. Nothing happens
foo()(); // The returned function is executed. Prints 5
Here we take advantage of a closure that is created on line 3. The function baz that we return from foo captures the context of its definition site (definition site = where the function is defined = in this case the foo function). So baz can be called from anywhere and still have access to the place in memory that bar points to.
The first invocation does nothing since the returned function is not called.
The second invocation calls the returned function therefore printing the number 5 to the console.
In order to understand your example snippet you can be more explicit in the function definition:
This:
const setCounterWithValue = value => () => {
setCounter(value)
}
can be rewritten like this:
function setCounterWithValue(value) {
return function() {
setCounter(value)
}
}
The instructor in the video just takes advantage of the implicit return feature of arrow functions
The code snippet just above will have the same result when used with your code. Hopefully, written like this, it makes more sense to you why there is no need for the anonymous function to have a value argument. It can access it via a closure.
As to why currying and high order functions are preferred by some JavaScript developers, the answer is way too long and opinionated, so I suggest you study the subject a bit more. A very interesting article can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
A bit harder to read but with a bunch of useful theory: https://eloquentjavascript.net/05_higher_order.html
It is easier to understand using function declaration instead of arrow functions. The tutorial suggests you refactor the function into a function factory:
function setCounterWithValue (value) {
return function () {
setCounter(value);
}
}
This means that calling the function above will return a function that requires no arguments:
let f = setCounterWithValue(1);
f(); // this sets counter to 1
Your suggested function instead returns a function that requires an argument:
function setCounterWithValue () {
return function (value) {
setCounter(value);
}
}
This means to set the value you will have to pass an argument:
let f = setCounterWithValue();
f(1); // set value to 1
The problem the refactor is trying to solve is that onclick functions are always called with an event object as the argument. When the button is clicked, the browser will call your function as:
setCounterWithValue(event);
You have no control over this. It is impossible to force the browser to call it any other way. Thus, ideally you want to pass into the onclick handler a function that ignores the event argument:
function setCounterWithValue (value) {
return function () {
setCounter(value);
}
}
let f = setCounterWithValue(counter -1); // this returns a function
// that ignores arguments
return <button onClick={f} />
most people would avoid using a temporary variable so:
<button onClick={setCounterWithValue(counter -1)} />
On the other hand, if you use your suggestion you would need to do:
{/* wrap in an anonymous function to ignore the event */}
<button onClick={() => setCounterWithValue(counter -1)} />
I just went through some react code, I am a bit confused about this arrow function.
I know drawer is the function name, open here is a parameter, what about () ? what does it use for? can someone explain it? Many thanks!
drawer = open => () => {
this.setState({
test: open,
});
};
I can call this function by this.drawer(true)
This is called a curried function.
It is the equivalent of the following code :
drawer = (open) => () => {
this.setState({
test: open,
});
};
This function needs to be called twice in order to be executed :
drawer('something')();
The first call : drawer('something') will return another function that you only have to call once with the open value already set up for you.
It is usually used to preconfigure callback passed to component props :
<DrawerOpener setOpen={this.drawer(true)}/>
Your component DrawerOpener can then call props.setOpen() to trigger an open event will not being able to chage the open variable of the function.
You are actually declaring a function which returns another function. An old-school equivalent of your code would be:
drawer = function(open) {
return function() {
this.setState({
test: open,
});
}
}
Note that if you use classic function instead of arrow functions, this won't reference your class instance without binding.
What you're seeing here is called currying, where one arrow function returns another arrow function. So in your example, the first function has the parameter open, it then returns a function that then accesses the first one's parameter by creating a closure.
Here's some more examples: https://scotch.io/tutorials/closures-and-currying-in-javascript
From the link above there's a really good example that illustrates what's happening here and how it can be used:
let greeting = function (a) {
return function (b) {
return a + ' ' + b
}
}
let hello = greeting('Hello')
let morning = greeting('Good morning')
hello('Austin') // returns Hello Austin
hello('Roy') // returns Hello Roy
morning('Austin') // returns Good morning Austin
morning('Roy') //returns Good Morning Roy
() indicates an empty arguments list.
drawer is a function that takes one argument (open). The return value is another function (which takes no arguments).
What is the difference between these function calling styles?
onPress = { () => { this.myFunctions() } }
And
onPress = { this.myFunctions() }
onPress={() =>{this.myFunctions()}}
You are passing an anonymous function which after onPress was invoked would call this.myFunctions()
onPress={this.myFunctions()}
You are passing into onPress the return value of this.myFunctions which means this gets executed everytime render is invoked by the component.
Both of the above ways of passing function into a React component are not advisable performance-wise. Using the above methods causes the function with the onPress to rerender everytime the parent renders because as it does a shallow comparison of previous anonymous function declaration it will result in two function not being equal as it is compared by reference.
It is advised to use below:
onPress={this.myFunctions}
where you are passing the reference of the function myFunctions. And whenever the parent rerenders, once the component has checked if new myFunctions is the same with previous render, it will return as true and will not render again the child.
You can use both
onPress={() =>{this.myFunctions()}}
//and
onPress={this.myFunctions} // without ()
If you want to pass arguments, you should use first one.
onPress={(e) =>{this.myFunctions(id, e)}}
There is a huge difference here.
onPress={this.myFunctions()}
The usage above means to evaluate the result of myFunctions and use the result in on press. This would only make sense if myFunctions() returned another function.
onPress={() =>{this.myFunctions()}}
The above means when onPress is triggered, then myFunctions is ran.
No, there's no real difference as long as it's just one function - although without the () (parentheses) in your second approach:
onPress={this.myFunctions}
If you want arguments:
onPress={e => this.myFunctions(e)}
I have seen a lot of reactjs places where a function is called like below
onChange = {this.fileSelected}
whereas I have seen its usage like below as well
onClick={() => this.clearDisplay()}
I want to ask if they both mean the same or is there any difference and what to use when.
If you use First:
onChange = {this.fileSelected}
It will only execute when onChange is called. If you want to bind this function then you have to declare it in the component class constructor like this:
constructor(props) {
super(props);
this.state = {
// your state
};
this.clearDisplay = this.clearDisplay.bind(this);
}
The Second one:
onClick={() => this.clearDisplay()}
This defines an anonymous function but, does not call it. Only when onClick is fired is it called. However, in some cases using an anonymous function can cause performance issues. That anonymous function will be defined on every render - and if you have a component that is re-rendering very often it can hurt the performance of your application. If you are sure that the component will not be rendered often, an anonymous function should be fine for convenience.
onChange={this.fileSelected}
Is preferable because it is able to not cause unnecessary re-renders.
onClick={() => this.clearDisplay()}
When you pass an anonymous function like this it will actually be called on all instances of the class instead of the one that the event was triggered on.
From a high level in may seem like they have the same behavior but if you were to use the second method consistently through a large codebase the performance of your application would suffer.
This article goes more in depth on the issue:
https://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36
onChange = {this.fileSelected}
This will direct bind fileSelected function to onChange method. so when onChange method called it will call fileSelected function.
while
onClick={() => this.clearDisplay()}
This will call onClick function in which you are calling clearDisplay function. so when you onClick method called, first anonymous called in which clearDisplay function called. So basically in this method two functions called. in this methos you can do additional calls or other things
e.g.
onClick={() => {
console.log("this function called")
this.clearDisplay();
}