通过onChange事件传递额外参数

90

我想做什么:

我正在尝试将子组件中的字符串传递给父组件的handleChange函数。

目前可行的方法:

我有一个父React JS类,其中包含以下方法:

handleChange(event) {

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

在渲染函数中,我有以下代码:

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

在Child类中,我有:

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

这个很好用。

我想要做的事情:

我试图向handleChange函数添加一个 section 参数,如下所示:

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

而在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>

现在我遇到了错误:

Uncaught TypeError: Cannot read property 'target' of undefined

第二个 console.log 语句中出现了这个错误。

我做错了什么?

此外,我正在考虑使 section 参数变为可选。如果是这样,有没有简单的方法可以实现?如果事件参数需要放在最后的话,似乎可能不可行。


@ShubhamKhatri 这不是这个问题的重复,因为我正在尝试从子类传递参数到父类。因此,我不能在<Child ...>元素的onChange属性中指定变量,因为它无法看到子组件内部的变量。 - kojow7
1
好的,我明白了,我会取消关闭投票。 - Shubham Khatri
请将解决方案发布为答案,而不是更新您的问题。这样可以帮助未来的访问者并避免混淆。谢谢。 - Bugs
7个回答

152

当你在写这个东西的时候:

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

handleChange会在触发渲染后立即被调用。

相反,应该这样做:

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

现在,当调用onChange处理程序时,handleChange将被调用。


10
我可能误解了,但是将箭头函数(或者 .bind)用作prop是不好的实践,会影响性能。在这里进行了解释:https://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36 - Davidicus

22

在处理事件时,使用双箭头函数,使用箭头函数时无需绑定:

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

而在孩子身上:

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

请注意,这需要babel插件“transform-class-properties”。 - Vael Victus

10
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")}
    />

8
作为原帖发布者,我最初将这篇文章作为我的问题的后续发布了出来,但它被删除并告知我改为回答发布,所以在这里:

根据Ritesh Bansal的回答,我学到了以下内容:

下面一行代码无法工作是因为在函数名后使用括号时,函数会立即调用而不是等待变化发生:

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

上述方法不起作用,以下的函数也一样不行:
<fieldset onChange={this.props.handleChange()}>

以上内容也将在第一次渲染时立即调用。

有两种解决方案:

不太好的方式:

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

更好的方式是:
<fieldset onChange={(evt) => this.props.handleChange("tags", evt)}>

问题现已解决。谢谢大家!
更新:
我还研究了Shubham Khatri的建议,将子元素更改为以下内容:
<Child handleChange={(e,val) => this.handleChange(e, val)}/>

我没有意识到在 render 函数中使用 bind 会导致每次调用 render 都创建一个新的函数实例。因此,我可以使用 Shubham Khatri 的方法或在构造函数中绑定方法。


<fieldset onChange={this.props.handleChange.bind(this, "tags")}>是最佳方式,当优化是考虑因素时,只需将绑定放在构造函数内部即可。 - Caleb Tolu
执行 (e, val) => this.handleChange(e, val) 和我的答案是一样的。在这两种情况下,一个函数返回另一个函数。 - François Alexandre COLOMBANI

7

不要在每个render()中定义匿名函数:

这里的大多数答案都建议在render()中定义一个匿名函数,但正如Davidicus所指出的那样,这是不推荐的:https://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36

François的答案避免了这个问题,但正如Vael Victus所指出的那样,这需要使用transform-class-properties

然而,他所做的只是定义了一个定义函数的函数,你也可以像这样做:

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

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

4
为了将子组件中的参数传递给父级组件,您可以在箭头函数中添加一个参数。
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>

示例代码片段

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>


在 <Child ... /> 元素中使用箭头函数相比于我已经有的东西会有什么优势吗? - kojow7

2
你可以这样做:
<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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接