Home > Software design >  Why can't we directly use setState() on onClick?
Why can't we directly use setState() on onClick?

Time:11-01

  const [state,setState]=useState(0)

  return(
    <>
    {state}
    <div onClick={setState(1)}></div>
    </>
  )

//Error
//Too many re-renders. React limits the number of renders to prevent an infinite loop.

the code works when we use {onClick={()=>setState(1)}} but I want to know why it don't work in the first case.

CodePudding user response:

The problem is not with the useState. You have to understand the function call. When you are giving the first parenthesis '()' to pass parameter, the function is being called automatically, as a result it is not waiting for the user to click.

CodePudding user response:

<div onClick={setState(1)}></div>

The above block is calling the 'setState' method as soon as the 'div' is rendered.

When the 'setState' function is called it alters the state and hence causes the element to render again, calling the 'setState' function instantaneously again causing the element to re-render in an infinite loop.

Also, the 'setState(1)' return a value(may be undefined) which is set as the value of 'onClick' property. When click event occurs, it cannot 'call' the returned value if it is not an executable function.

While this code,

<div onClick={()=>setState(1)}></div>

can be expanded as :

  <div onClick={function(){
    return setState(1)
  }}></div>

This code invokes a function which return the setState(1) function which can be called 'onClick' event.

CodePudding user response:

This happens because when you pass onClick={setState(1)} the actual value that will be used as the onClick callback function is the return of the call of setState(1). As this call results in a side-effect (state change), the component will be re-rendered and it'll be an infinite loop.

Why this happens?

The onClick prop receives a callback function. If you want that function to be called when the user clicks you should pass it like this:

<button
  onClick={() => setState(1)}
  //or
  onClick={setState}
>Click me!</button>

But if you use the second option, it'll receive the event (a SyntheticEvent type) and pass it to the setState() function. It'll be something like this:

<button onClick={(event) => setState(event)}>Click me!</button>

This happens because JSX is "transpiled" by React by converting all the props to key-value pairs and passing them to a function call(read more here about it). Consider the options:

<button
  // option 1:
  onClick={setState}
  // option 2:
  onClick={() => setState(1)}
  // option 3:
  onClick={() => {
    setState(1);
  }}
  // option 4:
  onClick={setState(1)}
>Click me!</button>

They'll be converted to something like this (roughly):

react.createElement(
  "button",
  {
    // option 1:
    onClick: setState,
    // option 2:
    onClick: () => setState(1),
    // option 3:
    onClick: () => {
      setState(1);
    },
    // option 4:
    onClick: setState(1),
    children: "Click me!"
  }
)
  • Option 1 will directly pass the function itself
  • Option 2 will pass the callback that triggers the setState() call and returns its result to the caller
  • Option 3 will pass the setState() but returns to the caller null, as nothing is returned from that
  • And finally, option 4 (your case) will pass to the callback the return of the setState() call and use that as a callback.

Why do I want this?

Eventually, you want to return a function from a function call. There are several uses for that but one simple one is creating a function based on some parameters such as:

const add = (to) => (value) => to   value;
const addTwo = add(2);
console.log(addTwo(3));
  • Related