Search code examples
javascriptreactjshigher-order-functions

Making simple React handleChange() function into Higher Order Function


function CheckoutForm({ onSubmit }) {
  const [shippingName, setShippingName] = useState("");
  
  function handleChange(func, e) {
    return function () {
      return func.call(null, e.target.value);
    };
  }
  
  return (
    <form>
      <div className="field">
        <label htmlFor="shipping-username">이름</label>
        <input
          id="shipping-username"
          type="text"
          name="shipping-username"
          autoComplete="off"
          value={shippingName}
          // onChange={(e) => setShippingName(e.target.value)} <--- ORIGINAL METHOD
          onChange={handleChange(setShippingName(e.target.value), e)} <--- MY QUESTION
        />
      </div>
    </form>
  )
}

While making a simple input field, I was curious if I could create a higher order handleChange function and reuse it for other inputs. I know there is a better way using computed property names, but just out of curiosity.

The error message I get from above code is that e is not defined and I am stuck here. Any advice is welcomed.


Solution

  • When you define an onChange handler like this:

    onChange={handleChange(setShippingName(e.target.value), e)}

    You're assigning the returned value of this handleChange call as the handler. You must therefore pass in your own values to handleChange, because it doesn't get called during the event -- e won't be defined at this point. It's only the resulting function that will receive e from the event.

    So you could re-define handleChange with e as the argument to the inner function, like this:

      function handleChange(func) {
        return function (e) {
          return func.call(null, e.target.value);
        };
      }
    

    And further down you can skip passing e (which woud be undefined anyway). Rather just do like this:

    onChange={handleChange(setShippingName)}
    

    ...however, I think you can greatly simplify everything by just defining your handler inline like this:

    onChange={(e) => setShippingName(e.target.value)}
    

    ^^ this is the way everyone does it.