Passing an additional parameter with an onChange event

ReactjsOnchange

Reactjs Problem Overview


What I'm trying to do:

I am trying to pass a string from a child component to the handleChange function of a parent component.

What currently works:

I have a parent React JS class with the following method:

handleChange(event) {

    console.log(event.target.value);
}

In the render function I have the following:

<Child handleChange={this.handleChange.bind(this)} />

In the Child class I have:

<fieldset onChange={this.props.handleChange}>
    <div>Tag 1: <input id="tag1" value={tags[0]} /></div>
    <div>Tag 2: <input id="tag2" value={tags[1]} /></div>
    <div>Tag 3: <input id="tag3" value={tags[2]} /></div>
</fieldset>

This works fine.

What I am trying to do instead:

I am attempting to add a section parameter to the handleChange function as follows:

handleChange(section, event) {
    console.log(section);
    console.log(event.target.value);
}

And in the Child class I have:

<fieldset onChange={this.props.handleChange("tags")}>
    <div>Tag 1: <input id="tag1" value={tags[0]} /></div>
    <div>Tag 2: <input id="tag2" value={tags[1]} /></div>
    <div>Tag 3: <input id="tag3" value={tags[2]} /></div>
</fieldset>

I now get the error:

Uncaught TypeError: Cannot read property 'target' of undefined

This error is being thrown in my second console.log statement.

What am I doing wrong?

Also, I am considering making the section parameter optional. If so, is there an easy way to do this? It seems like it might not be possible if the event parameter needs to be last.

Reactjs Solutions


Solution 1 - Reactjs

When you are writing this:

<fieldset onChange={this.props.handleChange("tags")}>

handleChange will be called immediately as soon as render is triggered.

Instead, do it like this:

<fieldset onChange={(e) => this.props.handleChange("tags", e)}>

Now the handleChange will be called when onChange handler is called.

Solution 2 - Reactjs

In your handle event use double arrow function, there's no need to bind when using arrow function:

handleChange = tags => (event) => {
    console.log(tags);
    console.log(event.target.value);
}

And in the Child:

<fieldset onChange={this.props.handleChange("tags")}>
    <div>Tag 1: <input id="tag1" value={tags[0]} /></div>
    <div>Tag 2: <input id="tag2" value={tags[1]} /></div>
    <div>Tag 3: <input id="tag3" value={tags[2]} /></div>
</fieldset>

Solution 3 - Reactjs

const handleChange = (tags) => (event) => {
// console.log(tags);
// console.log(event.target.value);
};

<input
      type="text"
      name="name"
      placeholder="Name"
      value={input.Iname}
      onChange={handleChange("Iname")}
    />

Solution 4 - Reactjs

As the OP, I had originally posted this as a follow up on my question, but it was deleted and I was told to post it as an answer instead, so here it is:

Based on Ritesh Bansal's answer, I have learned the following:

The following line was not working because when using parenthesis after the function name, the function is called immediately rather than waiting for a change to happen:

<fieldset onChange={this.props.handleChange("tags")}>

The above will not work, neither would a function such as this:

<fieldset onChange={this.props.handleChange()}>

The above would also get called immediately on first render.

There are two solutions to this:

The not-so good way:

<fieldset onChange={this.props.handleChange.bind(this, "tags")}>

The much better way:

<fieldset onChange={(evt) => this.props.handleChange("tags", evt)}>

The problem is now solved. Thank you everyone!

Update:

I also researched Shubham Khatri's suggestion to change the child element to this:

<Child handleChange={(e,val) => this.handleChange(e, val)}/>

I did not realize that using bind in the render function, that every time render is called it creates a new function instance. I can, therefore, either use Shubham Khatri's method, or bind the methods in the constructor.

Solution 5 - Reactjs

No anonymous function defined on each render():

Most of the answers here recommend an anonymous function defined in render(), which, as Davidicus pointed out, is not recommended: https://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36

François's answer avoids that problem, but as Vael Victus pointed out, that requires transform-class-properties.

However, what he's doing is just defining a function which defines a function, which you can otherwise do like this:

constructor(props) {
  super(props);
  this.handleChange = (yourSpecialParam) => (event) => this.handleChange(yourSpecialParam).bind(this)
}

render() {
  return <button onClick={this.handleChange(1234)} >Click Me</button>
}

Solution 6 - Reactjs

In order to pass a param from the child component to the parent you can take an argument to the arrow function.

handleChange(event, section) {
    console.log(section);
    console.log(event.target.value);
}
<Child handleChange={(e, val) => this.handleChange(e, val)} />

<fieldset onChange={(e) => this.props.handleChange(e, "tags")}>
    <div>Tag 1: <input id="tag1" value={tags[0]} /></div>
    <div>Tag 2: <input id="tag2" value={tags[1]} /></div>
    <div>Tag 3: <input id="tag3" value={tags[2]} /></div>
</fieldset>

Sample snippet

class App extends React.Component {
  handleChange(e, val) {
    console.log(e.target.value, val);
  }
  render() {
    return(
      <Child handleChange={(e,val) => this.handleChange(e, val)}/>
    )
  }
}

class Child extends React.Component {
  
  render() {
    return(
      <input type="text" onChange={(e) => this.props.handleChange(e, 'tab')}/>
    )
  }
}

ReactDOM.render(<App/>, document.getElementById('app'));

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Solution 7 - Reactjs

You can do this:

<fieldset onChange={(e) => this.props.handleChange("tags", e)}>
    <div>Tag 1: <input id="tag1" value={tags[0]} /></div>
    <div>Tag 2: <input id="tag2" value={tags[1]} /></div>
    <div>Tag 3: <input id="tag3" value={tags[2]} /></div>
</fieldset>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionkojow7View Question on Stackoverflow
Solution 1 - ReactjsRitesh BansalView Answer on Stackoverflow
Solution 2 - ReactjsFrançois Alexandre COLOMBANIView Answer on Stackoverflow
Solution 3 - ReactjsSana AlandView Answer on Stackoverflow
Solution 4 - Reactjskojow7View Answer on Stackoverflow
Solution 5 - ReactjskalzenView Answer on Stackoverflow
Solution 6 - ReactjsShubham KhatriView Answer on Stackoverflow
Solution 7 - ReactjsOmri AharonView Answer on Stackoverflow