onClick anonymous function with arguments vs onClick(function(arguments)) - javascript

I would like to know why in certain types of events, such as onClick, I have to declare an anonymous function to execute the onClick if my function contains arguments.
Example: https://codesandbox.io/s/suspicious-ramanujan-v998u
export default function App() {
const myValue = "MY VALUE";
const handleClick = (anyString) => {
console.log(anyString);
};
const anotherClick = () => {
console.log("It works");
};
return (
<>
<Button onClick={() => anotherClick()} color="primary">
Works without arguments <-- Works
</Button>
<Button onClick={anotherClick} color="secondary">
Works without arguments <-- Works
</Button>
<Button onClick={() => handleClick(myValue)} color="primary">
Works with arguments <-- Works
</Button>
<Button onClick={handleClick(myValue)} color="secondary">
Does NOT work with arguments <-- Does NOT work
</Button>
</>
);
}
Question
I do not understand why I have to create an anonymous function if the function I want to execute has arguments.
Why do I have to do onClick={() => handleClick(myString)}?
Why onClick={handleClick(myString)} is not enough?

onClick accepts a callback i.e. a function that will be called when the button is clicked.
anotherClick is a function so it works, as it's being called properly
() => anotherClick() is equivalent to this
...
const myTempFunction = () => {
anotherClick();
}
...
onClick={myTempFunction}
Since you're ultimately passing a function to onClick it works
() => handleClick(myValue) same reason as above, and equivalent code is
...
const myTempFunction = () => {
handleClick(myValue);
}
...
onClick={myTempFunction}
handleClick(myValue), now here you're actually calling the function the you're passing the value that the function returns to onClick, not the function itself,
the equivalent code would be
...
const myResult = handleClick(myValue); // This is the result of calling the function
...
onClick={myResult} // myResult isn't a function, it's the return value of handleClick(myValue)
Since you're not actually passing a function to onClick, onClick can't be called and it doesn't work

This is due to design of the syntax extension what we called JSX Template extension. When it comes to event Handling inside JSX, it needs a reference to the callback function which is going to trigger after specific event happens.
This is before JSX is converted in to JavaScript Via Babel
return (
<div>
<Button onClick={anotherClick} color="secondary">
Works without arguments
</Button>
<Button onClick={handleClick(myValue)} color="secondary">
Does NOT work with arguments
</Button>
</div>
);
This is after the Babel Conversion.
return React.createElement("div", null, React.createElement(Button, {
onClick: anotherClick,
color: "secondary"
}, "Works without arguments"),
React.createElement(Button, {
onClick: handleClick(myValue),
color: "secondary"
}, "Does NOT work with arguments"));
You can clearly see here that second click handler is executing the function directly rather than passing a callback function to React.createElement. Because internally React Element need a callback function to get pass to this function and you are just try to execute that function instead of parsing the reference.

You are supposed to specify a function in onClick={func}. "func" is only referring to a variable.
When saying onClick={func()}, you are adding the return value of func(), rather than specifying function func() { } to be executed, when the event is emitted.
When the button is clicked, the function, specified in onClick={} is executed.
This is what you are doing:
let emit = function () { };
button.addEventListener('click', emit());
When the button is clicked, it tries to call undefined as a function, since the function emit() has returned undefined.

In the doc for (Handling Component Events), there is an example
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
So we could see that every value passed to event handler props (onClick, onFocus,...) must be a method (or you may say function)
Go back to your example, in the first 3, you pass function, so it definitely works. But with the 4th, first to say handleClick is simply a method that return undefined, so when you call onClick={handleClick(myValue)}, it similar to onClick={undefined}, which resulted in nothing happened
const handleClick = (anyString) => {
console.log(anyString);
};
// ...
<Button onClick={handleClick(myValue)} color="secondary">
Does NOT work with arguments <-- Does NOT work
</Button>

Related

Is it necessary to use anonymous callback function in onClick handler? React

I'm currently learning the react hooks with an online course.
The instructor passed an anonymous callback function onto the onClick handler
return (
<div className="counter">
<button className="counter-action decrement" onClick={() => decrementScore()}> - </button>
<span className="counter-score">{score}</span>
<button className="counter-action increment" onClick={() => incrementScore()}> + </button>
</div>
);
But I don't understand why the anonymous callback is needed, and why I can't just pass the function by itself.
Following is what I tried and it worked ok without an error.
const Counter = () => {
const [score, setScore] = React.useState(0);
const incrementScore = () => {
setScore(prevScore => prevScore + 1);
}
const decrementScore = () => {
setScore(prevScore => prevScore > 0 ? prevScore - 1 : 0);
}
return (
<div className="counter">
<button className="counter-action decrement" onClick={decrementScore}> - </button>
<span className="counter-score">{score}</span>
<button className="counter-action increment" onClick={incrementScore}> + </button>
</div>
);
}
The additional anonymous callback isn't needed here. Doing it your way is fine.
It would be useful to have such an anonymous callback if:
The function being called was a member of an object, and it needs a this context of the object, and the function isn't bound. For example, the following would look pretty suspicious in a class component:
onClick={this.handleClick}
(see here for many pages on the subject)
You want to make sure the function is called with a particular argument or arguments, and not with others. For example, you might have
<button onClick={() => changeValue(1)}>click1</button>
<button onClick={() => changeValue(2)}>click 2</button>
Or, you might possibly want to omit the default React event that gets passed as the first argument to an event handler. But, in this case, no arguments are used in incrementScore or decrementScore, so it doesn't matter.
Additionally, note that you only need to use the callback form of a state setter, eg:
setScore(prevScore => prevScore + 1);
when the state value that the enclosing function closes over may be stale - if the state setter has been called previously, and the component hasn't re-rendered yet. But for a state that changes only once when a button is clicked, the state value has no chance of being stale. So, if you wanted, in this case, you could simplify
const incrementScore = () => {
setScore(prevScore => prevScore + 1);
}
to
const incrementScore = () => {
setScore(score + 1);
}

What the function type in Reac-Native

I have a function as below:
const myFunction = () => () => {
//do something
}
Does anyone help me explain what's means the function as above.
It's a function that returns a function. An example of where you might use this would be to reduce repetition when you need a set of similar callback functions. Here's an example from a recent React Native app that I've worked on: Consider a pocket calculator app that has a grid of similar buttons, each with an onPress callback that needs a distinct value to be used in the callback function:
// Snippet from React component
...
const onPress = value => () => {
console.log('You pressed ' + value);
}
return (
<>
<Button title="ONE" onPress={onPress(1)} />
<Button title="TWO" onPress={onPress(2)} />
<Button title="THREE" onPress={onPress(3)} />
</>
)
Each button gets an onPress callback function with a ()=>{} signature, as required, that does something with a distinct value. Rather than having to declare three functions, we've just declared one. Nice and tidy.

ReactJs What is the difference between calling {this.functionName} and {this.functionName()}

I have been using ReactJs for a couple of days now. And I find some syntax a bit curious.
For example, sometimes I have to call a function this way:
{this.functionName}
Without the parentheses at the end.
And sometimes I have to call it like this:
{this.functionName()}
Like in this example:
<button onClick={this.streamCamVideo}>Start streaming</button>
<h1>{this.logErrors()}</h1>
See the difference between calling this.streamCamVideo and this.logErrors().
Can someone please provide an explanation for this?
EDIT 1:
As requested, here are their definitions :
streamCamVideo() {
var constraints = { audio: true, video: { width: 1280, height: 720 } };
navigator.mediaDevices
.getUserMedia(constraints)
.then(function(mediaStream) {
var video = document.querySelector("video");
video.srcObject = mediaStream;
video.onloadedmetadata = function(e) {
video.play();
};
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
}); // always check for errors at the end.
}
logErrors(){
return(navigator.mediaDevices.toString())
}
{this.streamCamVideo} is a reference to the streamCamVideo function. You can think of this.streamCamVideo as a variable whose value is a function. Think about it like this:
const myVariable = 'some text'
const myOtherVariable = function() {
console.log("You are inside the myOtherVariable function");
}
Both myVariable and myOtherVariable are variables. One has the value of a string, the other has the value of a function. Let's say you want to pass both of these variables to another function:
const anotherVariable = function(aStringVariable, aFunctionVariable) {
console.log(aStringVariable, aFunctionVariable)
}
anotherVariable(myVariable, myOtherVariable)
You might see something like this logged to the console:
some text
[Function]
Notice that you don't ever see the text "You are inside the myOtherVariable function" logged to the console. That's because the myOtherVariable function is never called. It's just passed to the anotherVariable function. In order to call the function, you would need to do something like this:
const anotherVariable = function(aStringVariable, aFunctionVariable) {
aFunctionVariable()
console.log(aStringVariable, aFunctionVariable)
}
Notice the parentheses after aFunctionVariable()? That's what it looks like to actually call a function. So in this case, you'd see something like this logged to the console:
You are inside the myOtherVariable function
some text
[Function]
The function is actually being called.
So in your example:
<button onClick={this.streamCamVideo}>Start streaming</button>
<h1>{this.logErrors()}</h1>
this.streamCamVideo is just being passed as a variable to the <button> element. When the button is clicked, whatever has been assigned to onClick will be executed. That's when the function you passed as a variable will actually be called.
Also, notice the parentheses after this.logErrors()? The logErrors function is being executed. It is not being passed as a variable to anything.
{this.functionName} means referencing the function on a particular trigger. this way function will get called only when triggered.
{this.functionName()} is an actual function call, this method can be used to pass arguments. this function call will get called when page renders. This way function will get called repeatedly without any triggers. To stop that repeated function call we can use callback. like the following,
{() => this.functionName()}. this way the function will get executed only once.
{this.functionName} is used a reference type and it does not create instance on every render but {this.functionName()} is creates an instance of functionName on every render
<button onClick={this.streamCamVideo}>Start streaming</button>
Here if you use this.streamCamVideo Now it uses the reference type it does not create an instance of streamCamVideo but instead of if you use like this
<button onClick={()=>{this.streamCamVideo()}}>Start streaming</button>
Now it creates an instance of streamCamVideo instead of using the reference of streamCamVideo.
Creating an instance on every render it slows the performance of your application
Moreover, When evaluated, the first one is just a reference to the function, in the second case the function gets executed, and the expression will be evaluated to be the return value of the function.
We can use this.logErrors() when you want the function to be invoked and its result returned immediately.
In React, we typically follow this approach to split parts of your JSX code to a separate function for readability or reusability.
For Example:
render() {
someFunction() {
return <p>Hello World</p>;
}
return (
<div>
{this.logErrors()}
</div>
);
}
We can use this.streamCamVideo when you want only to pass the reference to that function to do something else.
In React, this is used while handling an event handler which can be passed down to another child-component via props, so that component can call the event handler when it needs to or when it gets triggered.
For Example:
class myExample extends React.Component {
streamCamVideo() {
console.log("button clicked!");
}
render() {
return (
<div>
<Button someCustomFunction={this.streamCamVideo} />
</div>
);
}
}
class Button extends React.Component {
render() {
return (
<button onClick={this.props.someCustomFunction}>Click me</button>
);
}
}
...
this.functionName(args) {
...
}
When its called like
... onClick={this.functionName}
The react component accepts like
function SomeReactComponent({ onClick }) {
...
so that onClick function can be called like
...
onClick(someEvent);
...
so that your function can use those args
...
this.functionName(someEvent) {
...
}
When it calls like this
... onClick={this.functionName()}
onClick accepts the result of functionName, which should also be a function in this case.
One is attribute, another with "()" is function.

Difference between function components and component classes?

This is following a tutorial in React JS.
class Square extends React.Component {
render() {
return (
<button
className="square"
onClick={() => this.props.onClick()}
>
{this.props.value}
</button>
);
}
}
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
above components do the same thing.there's a statement in the tutorial as follows
"When we modified the Square to be a function component, we also changed onClick={() => this.props.onClick()} to a shorter onClick={props.onClick} (note the lack of parentheses on both sides)."
I don't understand why there aren't parentheses on the right side in the function component? is it not a function call?
is it not a function call?
No, it isn't. It is a function. You are passing it to be the value of the onClick property.
This is a function that does nothing except call foo and return its value.
() => foo()
This is the function foo:
foo
The net result is more-or-less the same unless foo cares about the value of this or the arguments it gets.
If you added () to the short version, you would call the function immediately and pass the return value to onClick. Since that return value (probably) isn't a function, and you don't want the function to run until the click happens, that wouldn't be helpful.
function run_me(a_function) {
a_function();
}
function foo() {
console.log("foo has run");
}
run_me(foo);
run_me(() => foo());
run_me(foo());
When passing a handler we actually need to pass a reference to a function.
I don't understand why there aren't parentheses on the right side in
the function component? is it not a function call?
When you put parentheses you are invoking the function.
So when doing this:
onClick={props.onClick()}
We are passing the returned value of that function instead of a reference to it.

How can I get arrow functions working?

Getting my head around ES6 and was wondering how to create an arrow function which you can debug:
<button onClick={() => this.setState({open: !open})}>Toggle</button>
How can I put a console.log in here to see whether this has been triggered or not?
Use Braces{} for a arrow function which will have a block of code.
<button onClick={() => {console.log('hello');this.setState({open: !open})}}>Toggle</button>
Formatted
<button onClick={() => {
console.log('hello');
this.setState({open: !open})
}}>
Toggle
</button>
Hope this helps!
Well you could make it call a separate function, which console.logs and sets the state:
<button onClick={this.potato}>Toggle</button>
function potato() {
console.log('bombs away');
this.setState({open: !open})
}
Or you could put a print statement in the render function, since whenever you update the state it triggers a re-render
Your code is not valid HTML. The value of the onClick attribute needs to be a string, so it should be
<button onClick="() => this.setState({open: !open})">Toggle</button>
However, this will also not do anything, because merely executing a function declaration will not do anything. You probably just want
<button onClick="this.setState({open: !open})">Toggle</button>
Now it's easy enough to stick in a console.log:
<button onClick="console.log("here!"); this.setState({open: !open})">Toggle</button>
Of course, you are advised not to do things this way anyway. You should use addEventListener to set up the event handler. So it could be
const handler = () => this.setState({open: !open});
button.addEventListener('click', handler);
If you want to add a console.log to handler, as mentioned in another answer, you can either abandon the concise body form (without curly braces):
const handler = () => { console.log("here"); this.setState({open: !open}); };
Or, although not generally advised, you could use the comma operator:
const handler = () => (console.log("here"), this.setState({open: !open}));
The following idiom is also somethings useful:
const handler = () => console.log("here") || this.setState({open: !open});
which works because console.log returns undefined. This way, you can remove the log by simply deleting the characters console.log("here") || without worrying about fixing up braces and parens.

Categories