ChatGPT解决这个技术问题 Extra ChatGPT

React onClick function fires on render

I pass 2 values to a child component:

List of objects to display delete function.

I use a .map() function to display my list of objects(like in the example given in react tutorial page), but the button in that component fires the onClick function, on render(it should not fire on render time). My code looks like this:

module.exports = React.createClass({
    render: function(){
        var taskNodes = this.props.todoTasks.map(function(todo){
            return (
                <div>
                    {todo.task}
                    <button type="submit" onClick={this.props.removeTaskFunction(todo)}>Submit</button>
                </div>
            );
        }, this);
        return (
            <div className="todo-task-list">
                {taskNodes}
            </div>
        );
    }
});

My question is: why does onClick function fire on render and how to make it not to?


L
Long Nguyen

Because you are calling that function instead of passing the function to onClick, change that line to this:

<button type="submit" onClick={() => { this.props.removeTaskFunction(todo) }}>Submit</button>

=> called Arrow Function, which was introduced in ES6, and will be supported on React 0.13.3 or upper.


You can avoid these arrow function curly braces, also. Which I belive match the best practices: onClick={() => this.props.removeTaskFn(todo)}
Would you please explain this a little more? I get that people keep saying it is not best practice but I would like to understand what is happening here exactly with () => I understand what an arrow function is but not what this is () and why this is bad?
@wuno The () is the parameters of your anonymous function. It's empty here because we aren't passing in any parameters. Imagine that the () is the () of function(). Now regarding why it is not best practice for binding in the render() function is because on every render, we are rebinding the function to the component, which can be very costly.
@LongNguyen This is what I looking for! thanks a lot
Thank you so much, this problem was so obvious yet I didn't notice it, great answer!
P
Pete TNT

Instead of calling the function, bind the value to the function:

this.props.removeTaskFunction.bind(this, todo)

MDN ref: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind


IMHO and many others. You should favor stateless functions over clasess or binding because those can lead you into undesired side effects. Therefore, even being both of the answers correct, I believe that one is more apporpiate than the other.
Direct binding in render or anywhere else in the component is not recommeneded. Binding should happen always in constructor
B
Brian Swisher

The value for your onClick attribute should be a function, not a function call.

<button type="submit" onClick={function(){removeTaskFunction(todo)}}>Submit</button>

Don't use anonymous functions on event calls inside render - it will trigger another render
S
Sebastian

The Problem lies in how you pass your function

At the moment you are not passing the function but Calling it instead:

<Button onClick={yourFunction()} />

You can Fix this in two ways:

<Button onClick={() => yourFunction(params)} />

Or if you dont have any params:

<Button onClick={yourFunction} />

V
Vishal Bisht

JSX is used with ReactJS as it is very similar to HTML and it gives programmers feel of using HTML whereas it ultimately transpiles to a javascript file.

Writing a for-loop and specifying function as {this.props.removeTaskFunction(todo)} will execute the functions whenever the loop is triggered . To stop this behaviour we need to return the function to onClick. The fat arrow function has a hidden return statement along with the bind property. Thus it returns the function to OnClick as Javascript can return functions too !!!!!

Use -

onClick={() => { this.props.removeTaskFunction(todo) }}

which means-

var onClick = function() {
  return this.props.removeTaskFunction(todo);
}.bind(this);

o
olisteadman

For those not using arrow functions but something simpler ... I encountered this when adding parentheses after my signOut function ...

replace this <a onClick={props.signOut()}>Log Out</a>

with this <a onClick={props.signOut}>Log Out</a> ... ! 😆


Actually that's not working for me at all. I passed down the function and never added parenthesis and it still fired on render. Not sure if this has changed since Feb '19 but assigning a function like in Long Nguyen's and Vishal Bisht's answer fixed the issue.
I
Iheb Saad

you need to use an arrow function with onClick in order to prevent immediately invoke. so if your button looks like this :

<button onClick={yourfunctionname()} />

it must be like this :

<button onClick={() => yourfunctionname(params)} />

s
sudo bangbang

JSX will evaluate JavaScript expressions in curly braces

In this case, this.props.removeTaskFunction(todo) is invoked and the return value is assigned to onClick

What you have to provide for onClick is a function. To do this, you can wrap the value in an anonymous function.

export const samepleComponent = ({todoTasks, removeTaskFunction}) => {
    const taskNodes = todoTasks.map(todo => (
                <div>
                    {todo.task}
                    <button type="submit" onClick={() => removeTaskFunction(todo)}>Submit</button>
                </div>
            );
    return (
        <div className="todo-task-list">
            {taskNodes}
        </div>
        );
    }
});

S
Seyhak Ly

I had similar issue, my code was:

function RadioInput(props) {
    return (
    <div className="form-check form-check-inline">
        <input className="form-check-input" type="radio" name="inlineRadioOptions" id={props.id} onClick={props.onClick} value={props.label}></input>
        <label className="form-check-label" htmlFor={props.id}>{props.label}</label>
    </div>
    );
  }
class ScheduleType extends React.Component
{
    renderRadioInput(id,label)
    {
        id = "inlineRadio"+id;
        return(
            <RadioInput
                id = {id}
                label = {label}
                onClick = {this.props.onClick}
            />
        );

    }

Where it should be

onClick = {() => this.props.onClick()}

in RenderRadioInput

It fixed the issue for me.


N
Niv

It is possible to achieve this even in more readable way than:

<button onClick={() => somethingHere(param)}/>

const Comp = () => {
  const [triggered, setTriggered] = useState(false);

  const handleClick = (valueToSet) => () => {
    setTriggered(valueToSet);
  };

  return (
    <div>
      <button onClick={handleClick(true)}>Trigger</button>
      <div>{String(triggered)}</div>
    </div>
  );
};

That way it won't fire the state setter and won't cause too many re-renders compared to <button onClick={setTriggered(true)}/> which is okay if you don't have any params to pass to the function.


i
i_priyanshu

That's because you are calling the function directly instead of passing the function to onClick

If you have passed down onClick={onClickHandler()} then, the function onClickHandler() will be executed during the time of rendering too, the () instructs to execute the function as soon as it is rendered , which is not desired here , instead we use onClick={onClickHandler} , this will execute the onClickHandler only when the specified event occurs. But if we want to pass down a argument along with the function then we can make use of ES6 arrow function. For your Case :

<button type="submit" onClick={() => this.props.removeTaskFunction(todo)}>Submit</button>

M
Muhammad Muzamil

Bit late here but here is the simple answer.

direct approach will trigger by itself due to JS DOM rendering

onClick={this.props.removeTaskFunction(todo)}

anonymous arrow function approach. it will trigger on click

onClick={()=>this.props.removeTaskFunction(todo)}

L
L3xpert

You are not passing the function as an argument you are calling it directly that why it launches on the render.

HOW TO FIX IT

there are two ways:

First

<Button onClick={() => { 
this.props.removeTaskFunction(todo);
}
}>click</Button>

OR

Just bind it

this.props.removeTaskFunction.bind(this,todo);