React:如何将参数传递给回调函数

12

我有一个React组件内的元素列表,我希望它们可以被点击。当点击时,我会调用一些外部函数并传递项目ID作为参数:

render () {
  return (
    <ul>
      {this.props.items.map(item => (
        <li key={item.id} onClick={() => {doSomething(item.id)}></li>
      ))}
    </ul>
  )
}

这段代码可以工作,但它有一个很大的性能缺陷:每次调用render都会创建许多新的匿名函数。

我如何将doSomething函数作为引用传递,在仍然能够向其提供item.id的情况下?


@AndrewLi,我做不到,因为item.id在列表中,所以它是会变化的。 - Girafa
无论如何,你都需要使用bind或箭头函数(我更喜欢后者),它们都会创建新的函数...这真的是一个性能问题吗? - Andrew Li
@AndrewLi,考虑到这个包含成千上万个元素的列表。render本身被频繁调用,但是由于它在map中,匿名函数被更频繁地调用。我认为在小数据集上不会有任何问题,但是我正在寻找大数据集的解决方案。 - Girafa
2
我认为这是不可能的。可能有一种非常繁琐的方法,但我认为没有绑定或箭头函数的支持。 - Andrew Li
3个回答

16
你可以使用数据属性在使用相同函数的同时为每个项目设置正确的id:
function doSomethingFromEvent(event){
  return doSomething(event.target.dataset.id);
}

render () {
  return (
    <ul>
      {this.props.items.map(item => (
        <li key={item.id} data-id={item.id} onClick={doSomethingFromEvent}></li>
      ))}
    </ul>
  )
}

当您在元素中设置data-*属性时,可以使用dataset以哈希的形式获取它。例如,在doSomethingFromEvent中,我有event.target.dataset = {id: *id*}在MDN上查看更多信息 当更新哈希(例如状态)时,使用<li key={item.id} data-myattriute={myvalue} onClick={this.handleClick}></li>会更加清晰简洁,我只需定义handleClick,如下所示:
handleClick(event){
    // Here event.target.dataset = {myattribute: myvalue}

    Object.assign(myObject, event.target.dataset);
    // or
    this.setState(event.target.dataset);
}

回到你的问题,这种方法的好处在于,如果你确保容器元素 (ul) 不能在其带有数据属性 (li) 的子元素外被点击,也就是你的情况,你可以在它上面声明函数:

render () {
  return (
    <ul onClick={doSomethingFromEvent}>
      {this.props.items.map(item => (
        <li key={item.id} data-id={item.id}></li>
      ))}
    </ul>
  )
}

现在你的函数只创建一次,并不会在每个项目中重复。


1
你可以创建一个部分应用或高阶函数来封装item.id并将其传递。让我们看一个玩具示例:
class App {

   partiallyApplied = id => e => {
     console.log(id,'this is passed in first')
     console.log(e,'this is passed in second')
   }

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

}

现在您可以访问1234以及您的event对象。
这是使用transform-class-properties Babel插件。如果不能或不想使用它,您可能可以尝试这样做:
partiallyApplied(id){
  return function(e){
   console.log(id,'this is id')
   console.log(e,'this is event')
  }
}

但是在调用时,您将不得不绑定this,我不喜欢到处都这样。


4
看看它如何影响性能会很有趣。因为它似乎无论如何都会为每个项创建新函数,就像使用bind或箭头函数一样。 - dfsq
1
每次它都会返回一个新的函数。不过,我不确定是否有办法避免这种情况,因为我们想要为每个“id”使用一个新的闭包。 - Tim Roberts

1
你可以为数组中的每个项目创建一个新组件,使用 props 如 这样
class Li extends React.Component {
  render() {
    return <li onClick={this.onClick}> {this.props.children} </li>;
  }
  onClick = () => {
    console.log(this.props.item);
  };
}

class App extends React.Component {
  state = {
    items: [
      {id: 1, name: 'one'},
      {id: 2, name: 'two'},
      {id: 3, name: 'three'},
    ]
  };
  render() {
    return <ul> 
      {this.state.items.map(i => 
        <Li key={i.id} item={i}>{i.name}</Li>
      )} 
    </ul>;
  }
}

也许使用绑定或箭头函数为每个项目创建新函数会比这种方法表现得更差? - dfsq
我个人不喜欢这种解决方案,因为现在底层组件知道它需要调用的函数和提供的所有参数。这违反了单一职责原则。 - Girafa
1
@Girafa 这是真的。但如果您有一个使用案例,每次呈现每个项目都创建一个新上下文太昂贵,那么用8行代码打破这个原则可能是值得的。 - Tholle
@dfsq 虽然最初的渲染可能与箭头函数一样昂贵,但不同之处在于,如果 <Li> 是一个 PureComponent,则由于其 props 不会改变,它不会在每次迭代时重新渲染。然而,在箭头函数的情况下,props 确实会改变 - 每次都会创建一个新函数并进行渲染。 - VitalyB

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