DEV Community

Cover image for onClick handlers with Currying (React)
Nitsan Cohen
Nitsan Cohen

Posted on

onClick handlers with Currying (React)

How do you pass a function with an argument to an onClick event in React?

You are probably all familiar with the fact that when passing a function that takes an argument to an onClick event (or any other event), you have to wrap it with an anonymous callback function.

For instance:

<button onClick = { () => myFunction(argument) } />
Enter fullscreen mode Exit fullscreen mode

We do it because we want to have a pointer to a function triggered upon a click. If we pass the function without a callback, it will get fired right when the DOM is mounted.

But there is another option. We can pass an anonymous callback to our handler. This way, our handler itself will serve as a pointer, and we will not have to give an anonymous function to our onClick event. The name of this design pattern is called currying.

const myHandler = (text:string) => () => alert(`${text}`)
Enter fullscreen mode Exit fullscreen mode

When is this useful?

It could be helpful when we intend to use our handler in many events (for example, mapping an array). Instead of creating useless callback functions for each event, we create them only once in the handler.

currying react example

Top comments (5)

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

But there is another option. We can pass an anonymous callback to our handler. This way, our handler itself will serve as a pointer, and we will not have to give an anonymous function to our onClick event. The name of this design pattern is called currying.

This is very confused - you are still passing an anonymous function to the onClick event - albeit one created by a higher order function - there are no 'pointers' involved. Also, you haven't curried anything here. Currying is the act of transforming a function that takes multiple arguments into a series of functions that each take 1 argument.

The function you have named myHandler isn't a handler at all - it is a function that creates a handler. It would be better named createHandler.

An example of currying a function:

// 'normal' function style
function add(a, b) {
  return a + b
}
function addCurried(a) {
  return function(b) {
    return a + b
  }
}

// or 'arrow' function style
const add = (a, b) => a + b 
const addCurried = a => b => a + b

// usage
add(1, 2)   // 3
addCurried(1)(2)   // 3
addCurried(7)   // function

const add5 = addCurried(5)
add5(3)   // 8
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jamesthomson profile image
James Thomson

Thank you! I was reading through this and thinking... this isn't currying at all.

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

You are probably all familiar with the fact that when passing a function that takes an argument to an onClick event (or any other event), you have to wrap it with an anonymous callback function.

This is confused, and incorrect. If you put myFunction(x) as an event handler, you are not passing a function. You are calling a function, and passing the result of that call as the event handler. If you actually pass a function - then you don't need to wrap it at all. In fact, most functions you would use as event handlers are functions that take an argument - that argument being the event object itself.

Collapse
 
stevereid profile image
Steve Reid

It would’ve been helpful for me if the post contained a code example of use case suited to currying of a handler as I tend to gain a clearer understanding from the code.

Collapse
 
b33hazard profile image
MUHAMMAD ALI
const Register = () => {
  const [fullName, setFullName] = useState("");
  const [email, setEmail] = useState("");

  const handleChange = (setState) =>
    (e) => setState(e.target.value);

  console.log("Name is: ", fullName)
  console.log("email is: ", email)

  return (
    <>
      <h1>Register </h1>
      <div>
        <label htmlFor="name">Name: </label>
        <input
          type="text"
          name="name"
          value={fullName}
          id="name"
          placeholder="Name"
          onChange={handleChange(setFullName)}
        />
      </div>
      <div>
        <label htmlFor="email">Email: </label>
        <input
          type="email"
          name="email"
          value={email}
          id="email"
          placeholder="Email"
          onChange={handleChange(setEmail)}
        />
      </div>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode