React onClick function triggering itself at mount - javascript

I have a component that contains a basic onClick function.
The function is called when the component is rendering (without any click), why is it happening?
import React, { Component } from "react";
class Some extends Component {
constructor(props) {
super(props);
}
someFunc = (message) => {
console.log(message)
}
render() {
return (
<p onClick={this.someFunc('hello')}>test</p>
)
}
}

in React you need to pass unexecuted functions, so either
onClick = {this.someFunction}
or if you need to pass an argument
onClick = {() => this.someFunction('argument')}

You don't have to append your function like that. You just need to call it by other ways:
<p onClick={() => this.someFunc('hello')}>test</p>
By using this syntax you can call it by params.
But your first solution was just passing the function
If your function had no argument to pass you could just pass the function like below:
<p onClick={this.someFunc}>test</p>
Because you need to pass some arguments the first one which I mentioned will be your desired one.
You can read their document for further details: React Handling
Events

The reason is because you write
<p onClick={this.someFunc('hello')}>test</p>
instead of
<p onClick={this.someFunc}>test</p>.
If you put there the () the function will be called right on rendering, else only if clicked.
Try this arrow function
<p onClick={() => this.someFunc('hello')}>test</p>
Not tested, but should work correctly this way.

tl;dr: You are invoking this.someFunc('hello') when you render your class, not when you call your onClick property. To fix it, you should use an arrow function, as so:
<p onClick={() => this.someFunc('hello')}>test</p>
Now if you want to know why this happens, let me clarify what you are doing.
import React, { Component } from "react";
class Some extends Component {
constructor(props) {
super(props);
}
// You define a class method that when called will in turn call the
// console.log method, passing message as your argument.
// message will equal 'hello' in your example.
someFunc = (message) => {
console.log(message)
}
// You define a render method of your class, react will
// automatically call this method on render.
render() {
// Your render method (which again is called automatically) will return
// <p onClick={this.someFunc('hello')}>test</p>
// let's see what operations you are performing in this JSX return.
return (
// You first define a paragraph html element to be rendered.
// You then give your element an attribute called onClick.
// But oh no! You are assigning onClick with the value of:
// this.someFunc('hello')
//
// That is not good, as what you are in effect saying is:
// please call the method this.someFunc and pass a single argument: 'hello'
// then assign the return of that method call to my property named onClick.
// Therefore, every time you render this class, you are asking
// javascript to call this function and get its value (which is undefined).
// So while you think onClick is a function, it is not! it is only a value.
// A value which you are asking JavaScript to get for you on each render.
// This is because in JS, when you write functionName(),
// you are literally calling the function at that moment.
// See https://www.w3schools.com/js/js_function_invocation.asp
<p onClick={this.someFunc('hello')}>test</p>
// What you want to do instead is assign your onClick property
// a function! Not a value!
// You want to write, "When I click here, do: this.someFunc('hello')"
// to do that, you have some options, but the end goal will be the same
// What you need to do is assign onClick a value, which when called (onClick)
// will trigger your function! For instance:
// onClick={() => this.someFunc('hello')}
// Above we say "on each render, assign the onClick property a value which IS a function!"
// This is, an unnamed arrow function!
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
// Notice how we have defined an unnamed function that wraps our
// this.someFunc('hello') and we have NOT
// immediately called it (no parentheses following the wrapper function)
// # outer function \() => #inner function \functionName()\ \ <-- No trailing parentheses.
// Therefore, now the only way to execute your method named
// this.someFunc('hello') is by calling its wrapper function.
// And the only way to call that unnamed wrapper function?
// By invoking your onClick property of your p element.
)
}
}

Related

How calling a callback directly fixes the 'this' in a React Class Component?

I would like to learn how is the value of 'this' set when a function called in JSX as a callback to an eventHandler. I noticed that when I call it directly there is no issue accessing state without getting the famous undefined 'this' error, like so:
import React from "react";
class Accordion extends React.Component {
state = { term: "random term" };
onTitleClick() {
console.log("Title is clicked");
console.log(this.state.term);
}
render() {
const renderedItems = this.props.items.map((item) => {
return (
<React.Fragment key={item.title}>
<div className="title active" onClick={this.onTitleClick()}>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className="content active">
<p>{item.content}</p>
</div>
</React.Fragment>
);
});
return <div className="ui styled accordion">{renderedItems}</div>;
}
}
export default Accordion;
When you pass it as just a reference, the famous 'this' is undefined error comes back. Then we know how to bind the 'this' and so on. I feel like I just memorized the solution and now would like to learn the difference.
onClick={this.onTitleClick()} - This is not how you set the event listener. You just need to pass the name of the function instead of calling it yourself.
As far as your question regarding the value of this is concerned, value is set depending on how the function is called. This is not specific to React, this is just how value of this is set in Javascript.
I noticed that when I call it directly there is no issue accessing
state without getting the famous undefined 'this' error
That's because when you call it like this: this.onTitleClick() - onTitleClick() is called on this which refers to the Accordion component. But as mentioned at the start of this answer, this is not how you set the event listener. Instead of calling this method yourself, you need to let javasctipt call it.
When you pass it as just a reference, the famous 'this' is undefined
error comes back
This is the correct way to add an event listener but you get an error because when javascript calls the event handler function, value of this is not your component, i.e. Accordion.
To solve this issue, you have two options:
Explicitly set this using .bind()
this.onTitleClick = this.onTitleClick.bind(this);
Use arrow functions instead of regular functions as event handlers
onTitleClick = () => {
console.log("Title is clicked");
console.log(this.state.term);
}
Following are couple of related questions that might help in understanding this further:
“this” is undefined inside an anonymous function returned by another function React
How does React share methods between classes(components)
When you pass it as a reference, the this is undefined. In order for this to work, you need to bind the function to the class. You can do that in the constructor
constructor(props) {
this.state = { term: "random term" };
this.onTitleClicked = this.onTitleClicked.bind(this);
}
Also, when you pass it to the component, don't call the function, just pass it
<div className="title active" onClick={this.onTitleClick}>
Notice missing parenthesis by the this.onTitleClick call.

Using () => vs this.something in jsx

I am trying to comprehend when do we use something like in jsx of our react
something = {this._onRefresh}
something = {() => this._onRefresh}
something = {this._onRefresh()}
Where something could be something we are passing to our
child component
onChange Event
onClick event
Any other situation you can think about
Where our ._onRefresh() could be
._onRefresh = () => {
//changes state of our react component
//Call redux action
//call other functions
}
or In case of forms, it takes in event which triggered it
._onRefresh = (event) => {
//takes target value to search inside the data
//Store data in the backend
}
Can someone please explain me when do we need to use which one? will help me a lot in clearing my fundamentals of react and javascript
The points 1/2 and 3 are actually totally different.
Your point 3 executes a function and assigns the returning value to the something.
Your examples 1/2 assign a function to something
The case 3 could be used when for example you have the disable attribute, and you want to assign the returning true/false of a function.
The points 1 and 2 are assigning a function to the attribute, like for example with onClick attribute, which accepts a callback to be called on click.
The difference between the first and the second point is that if you put that code inside the render method, the second point creates a function for every render, which is not the best for performances.
Using the first point, you should pay attention on how you define the method for the reference of this inside the method.
If you define the class method as:
myMethod() {
console.log(this); // it will be undefined by default
}
Then you need to bind the this inside the constructor if you want to access this inside the method:
this.myMethod = this.myMethod.bind(this);
If you define the method as arrow function, it will keep the value of the this inside the method, so no need of the bind:
myMethod = () => {
console.log(this);
};
1- you are passing a function as a property to this component
2- you are creating a new function and passing it as a property to this component
3- you are passing the result (output) of calling _onRefresh as a property to this component
Option 1 is valid if _onRefresh is actual callback function that should be passed via a prop:
_onRefresh = () => console.log('refresh');
...
<Component onRefresh={this._onRefresh}/>
Where Component uses onRefresh like:
// do refresh
props.onRefresh();
Option 2 is valid if _onRefresh is actual callback function, and onRefresh prop is expected to be higher-order function by a component that accepts a callback:
_onRefresh = () => () => console.log('refresh');
...
<Component onRefresh={() => this._onRefresh}/>
Where Component handles onRefresh like:
// do refresh
const refreshFn = props.onRefresh();
refreshFn();
Option 3 is valid if _onRefresh is higher-order function that returns another function, and this is expected by a component that accepts a callback:
_onRefresh = () => () => console.log('refresh');
...
<Component onRefresh={this._onRefresh()}/>
Where Component handles onRefresh like:
// do refresh
const refreshFn = props.onRefresh();
refreshFn();
Scenarios in options 2 and 3 are much less probable because they don't have much uses in this particular case in React.
In this case option 1 is likely correct because _onRefresh does not return another function and is not expected by child component. Option 2 is a mistake that will result in _onRefresh being never called. Option 3 is a mistake that will result in _onRefresh being called instantly and undefined is not a function error later or, even worse, erroneous behaviour with no error.

What is the point of wrapping a function of react component if i want to pass it down as a property?

What is the point of this?
In the next example i found in book code we have a funtion in the component that changes component state createTimer()
createTimer = (timer) =>
{
const t = helpers.newTimer(timer);
this.setState({
timers: this.state.timers.concat(t),
});
client.createTimer(t);
};
It is wrapped:
handleCreateFormSubmit = (timer) => {
this.createTimer(timer); };
And passed down as property:
<ToggleableTimerForm
onFormSubmit={this.handleCreateFormSubmit}
/>
If you just do this:
<ToggleableTimerForm onFormSubmit={this.createTimer}/>
...and createTimer is a regular method of your class:
class YourComponent extends Component {
createTimer(timer) {
const t = helpers.newTimer(timer);
this.setState({
timers: this.state.timers.concat(t),
});
client.createTimer(t);
}
}
...then the issue would be that when the child component calls onFormSubmit, this wouldn't be set correctly.
But since you're setting a property of your instance and are using an arrow function:
class YourComponent extends Component {
createTimer = (timer) => {
const t = helpers.newTimer(timer);
this.setState({
timers: this.state.timers.concat(t),
});
client.createTimer(t);
};
}
...you don't have to worry about this being bound correctly, so you're right that you don't need the wrapper to fix that. Perhaps the wrapping function is there as a precautionary measure since the class method pattern is more common.
The only benefit you'd gain is if the child calls this.props.onFormSubmit with additional arguments that you want to ignore. If that's not the case, then you can leave out the wrapping function.
Generally you pass a function down that's bound to it's original component. This allows child components to alter the state of their parent. Imagine this Scenario :
I have a parent component with state property A. I have a function that takes an input and updates the state of the PARENT!!!!
I pass that as a prop to a child (maybe a form). When I submit the form, I call the function passed as a prop to update the PARENTS state with my form values.
A few things to keep in mind, lexical arrow functions LACK SCOPE, and if the function leverages the state of the component it must be bound to the component.
One problem I see in your code....
handleCreateFormSubmit requires a parameter. onFormSubmit will pas it one, but I don't think it'll be the one you're expecting. It'll pass the event. You can do something along these lines "
onFormSubmit={() => this.handleCreateFormSubmit(someValue)}

Arguments undefined after passing to React function?

In the Home component we want to call a function:
refreshMatchStatus = (match_id, status) => {
console.log(match_id)
var matches = this.state.matches
matches.map(function(match){
if (match.id == match_id){
match.challenged = status
}
})
this.setState({matches: matches})
}
From the Home render this function is passed down to the next component:
<List refreshMatchStatus={this.refreshMatchStatus.bind(this)} showAlert={this.props.showAlert} user={this.state.user} token={this.state.token} matches={this.state.matches}/>
The function is then passed down through some components like this:
refreshMatchStatus={this.props.refreshMatchStatus}
When it arrives in the ChallengePopup component it is executed like this:
this.props.refreshMatchStatus(match_id, status)
For some reason the arguments match_id and status are undefined when they are passed.
If we do a console.log in the ChallengePopup component, one line before the function is called, the match_id will return the right id number. But when we do a console.log on the first line of the refreshMatchStatus function, the match_id returns undefined.
We suspect this has something to do with the bind(this), but we cannot find any way to pass the arguments in the right way.
Move your bind call to the constructor e.g.
constructor(props) {
super(props);
this.refreshMatchStatus = this.refreshMatchStatus.bind(this);
}
Then you can remove the .bind call in your render method, otherwise since its the parent component, every time a fresh render is triggered the method binding is reset which I believe can result in loss of arguments from children.

React bind(this, value) in opposite order on function declaration

I have an onClick handler function that passes in the click event and index arguments to the function.
However, turns out I have to declare things in opposite order.. as such:
render(){
return(
<button onClick={this.deleteItem.bind(this, index)}Click Me</button>
)
}
deleteItem(index, e){
e.preventDefault();
this.props.dispatch(deleteTodo(index));
}
This works without issue. But why must this and index be switched in deleteItem()? Is this documented somewhere I am unaware of?
this and index are not being switched. What you're seeing is the difference between using bind to bind a this value and calling the function directly.
In short, if you were to call the function directly, like so:
<button onClick={this.deleteItem}Click Me</button>
You would get the arguments in the order in which you declared them in the function. But, as you obviously know, calling the function in this way leaves you with a this variable referring to the context in which the event handler was called, not the actual class you have declared.
What you're doing instead is calling this.deleteItem.bind, which is necessary to ensure that the this variable is properly bound when it is called in the context of the event.
The better way to do this is the way shown in the React documentation on ES6 classes: bind the event handler in the constructor of the class.
For example,
export class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.initialCount};
this.tick = this.tick.bind(this);
}
tick() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div onClick={this.tick}>
Clicks: {this.state.count}
</div>
);
}
}
By binding the event handler method in the constructor, it is clearer, and you don't have to worry about the context when you use it.
EDIT
Sometimes, you need to pass specific data to your event handler. This is possible using the constructor binding method here. Just do something like this:
constructor(props) {
super(props);
this.myHandler = this.myHandler.bind(this);
}
myHandler(importantValue, event) {
console.log(importantValue); // will log 'pass this value!'
// and you have access to the event object, if you pass it along
// if you don't need the event object, then just leave it out
}
render() {
<div onClick={(event) => { this.myHandler('pass this value!', event); }>
Click Me!
</div>
}
By adding an anonymous function as your event handler in the onClick property for your element, you can do anything you want!
Check the documentation for bind(). bind() fixes/binds the context (this keyword) of the function to the object specfied in the first argument, and the rest of the arguments are prepended to the bound function. That's why index appears before event.

Categories

Resources