Passing an additional parameter with an onChange e

2019-03-18 06:40发布

问题:

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.

回答1:

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.



回答2:

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>


回答3:

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.



回答4:

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>
}


回答5:

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>


回答6:

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>