7

I am trying to write a (curried?) onChange event handler on a Component that will receive a key argument which will let it know which key in the state object to update. The code won't compile, saying 'key' is not defined.

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      firstName: null,
      lastName: null
    }
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange = (key) = (event) => {
    console.log(key, event);
  }

  render() {
    return (
      <div>

        <form>
          <input onChange={this.handleChange('firstName')} value={this.state.firstName} />
          <input onChange={this.handleChange('lastName')} value={this.state.firstName} />
        </form>

        {JSON.stringify(this.state, null, 4)}
      </div>
    );
  }
}
NoobOfNoobs
  • 75
  • 1
  • 5
  • 16
    handleChange = (key) => (event) => – madox2 May 11 '17 at 13:13
  • why you are making it so complex, use this: `onChange={(e) => this.handleChange('firstName', e)}` then use `handleChange` like this: `handleChange(key, e){console.log(key, e)}` – Mayank Shukla May 11 '17 at 13:17
  • @madox2 Thanks! I can't believe I missed that. – NoobOfNoobs May 11 '17 at 13:30
  • 5
    @MayankShukla Because using a callback there means that a function is created every re-render, which means more work for the garbage collector (or so I am told). Also I wanted to learn about currying! – NoobOfNoobs May 11 '17 at 13:32
  • You won't be saving memory with this, unless you then [`memoize`](http://stackoverflow.com/questions/30386943/how-to-create-a-memoize-function) the returned functions per their key :). Inline functions pose a different problem... they cause PureComponent checks to fail as every reference is different. – hazardous May 11 '17 at 13:40
  • And sorry to be a Killjoy but this aint no [currying](http://stackoverflow.com/questions/36314/what-is-currying) too :p, you have created a function factory, which is fun nonetheless! – hazardous May 11 '17 at 13:44
  • 1
    @hazardous: I'm curoious - where do you draw the line between a function factory and currying? The code written by the OP seems pretty much identical to the accepted answer on the "what is currying" question you linked (minus the arrow functions). – Joe Clay May 11 '17 at 13:53
  • Simple, currying is taking a function with several params and wrapping it in a way to allow only one param at a time. If a function only takes one param, that's not currying. The linked example (now I realize), stops short on defining the curry translation function which takes the multiparam function and returns a curried version. – hazardous May 11 '17 at 14:08
  • From https://wiki.haskell.org/Currying: "Currying is the process of transforming a function that takes multiple arguments into a function that takes just a single argument and returns another function if any arguments are still needed." OP had a function that took two arguments, "key" and "event", and decided to transform that to a function that takes one argument, "key", and returns a function because more arguments are still needed. That's currying. – noppa May 11 '17 at 14:18
  • The event handler is already a series of functions taking one argument each. An important part of currying is this - `f(a,b) = f(a)(b)`. The first form is absent. Anyways, I was nudging the OP towards exploring the translator mechanisms in JS to curry a multi-param function. @NoobOfNoobs: please check this out: https://medium.com/@kevincennis/currying-in-javascript-c66080543528! – hazardous May 11 '17 at 14:47

2 Answers2

3

You have to pass both the event as well as the key on the OnChange handler.

Do this

<input onChange={this.handleChange.bind(this,'firstName')} value={this.state.firstName} />

And the onChange should be

 handleChange = (key, event) => {
    console.log(key, event);
  }

This will allow both the event and key to be passed and the function will work.

mrinalmech
  • 2,065
  • 1
  • 20
  • 18
  • Your solution *does* create a new function per render. That's exactly what [Function.prototype.bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) does. – noppa May 11 '17 at 14:09
2

I think that in your code you have just forgotten to put the arrow function after initializing first function name:

handleChange = (key) = [!!! HERE - should be an arrow !!!] (event) => {
  console.log(key, event);
}

Try to use this:

handleChange = (key) => (event) => {
  console.log(key, event);
}

So this way your first function which has the param key will return another function with param event.

V. Sambor
  • 12,361
  • 6
  • 46
  • 65