React.js:在组件中没有bind()的情况下将参数传递给事件处理程序的最有效方法

Hir*_*oki 14 javascript reactjs

当您必须this在事件处理程序中使用关键字时,必须将函数(事件处理程序)与thiskerword绑定.否则,您需要使用箭头功能.

例如

//This function isn't bound whilst using "this" keyword inside of it.
//Still, it works because it uses an arrow function
handleClick = () => {
    this.setState({
      isClicked:true
    });
}

render() {
    return (
      <div className="App">
        <button onClick={this.handleClick}>Click</button>
      </div>
    );
}
Run Code Online (Sandbox Code Playgroud)

但是,使用上述方法,您无法传递参数.你需要使用......

  1. bind(this, param) 功能之后
  2. 匿名箭头功能

<button onClick={this.handleClick}>Click</button>
will be
<button onClick={this.handleClick.bind(this, 111)}>Click</button>
or
<button onClick={() => this.handleClick(111)}>Click</button>
Run Code Online (Sandbox Code Playgroud)

这是个问题.

将参数传递给事件处理程序的最有效方法是什么?

根据官方文档,使用bind()可以破坏性能,因为......

每次组件渲染时,在render中使用Function.prototype.bind都会创建一个新函数

使用匿名箭头功能也是如此.医生说......

在渲染中使用箭头函数会在每次渲染时创建一个新函数

那么,传递参数的最有效方法是什么?

任何输入将不胜感激.

PS

有人问过如何param下定决心.这将动态确定(即并非总是如此111).因此,它可以来自状态,道具或此类中定义的其他一些函数.

Bho*_*yar 10

我在另一篇文章中解释了它:在react组件中单击事件.

如果您担心其性能,切勿使用内联箭头功能.您仍然可以使用公共类方法并绑定上下文this.

handleClick = () => () => {
    this.setState({ // this works now
      isClicked:true
    });
}
Run Code Online (Sandbox Code Playgroud)

您可以像这样传递任何您喜欢的参数:

handleClick = (param1, param2, param3) => (event) => {
Run Code Online (Sandbox Code Playgroud)

根据devserkan的评论,

这是和其他选择相同的.此函数也在每个渲染中重新创建.

不,它没有.请参阅文档中的说明:

如果将此回调作为prop传递给较低组件,则这些组件可能会进行额外的重新呈现.我们通常建议在构造函数中使用绑定或使用类字段语法来避免这种性能问题.

另外,请参阅bigga-hd的评论,该评论低于特定业绩的答案:

避免在渲染中声明箭头函数或绑定以获得最佳性能.在render之外声明你的函数.每个渲染都不再有功能分配.


你怎么称呼这个处理程序?

您可以像这样调用方法:

onClick={this.handleClick(param1,param2,param3)}
Run Code Online (Sandbox Code Playgroud)

PS:我没有将这篇文章标记为重复,因为问题范围明显不同.所以,只是链接帖子,让你深入了解更多细节.

  • 好吧,让我们停下来。但看到这一点,很难过。建议在箭头一或绑定上使用柯里化函数。这是**相同**的事情。在文档中,他们不使用柯里化函数,这就是它起作用的原因。每当您在使用的回调中看到 `()` 时,这意味着回调被重新创建,并且因为它是重新创建的组件,这些组件接受此认为是更改的道具并重新渲染它们。 (7认同)
  • 这是和其他选择相同的.此函数也在每个渲染中重新创建. (3认同)
  • 我可以看到你已经更新了答案,但这并没有回答我的问题.Docs对于currying没有说什么.如果我没有弄错的话就是这样.因此,您在回调处理程序中使用curried函数,并在每个渲染中重新创建它. (3认同)
  • 也许你是那个误解它的人:)公共类字段建议只是绑定的替代方法,就是这样.它与重新渲染无关.您可以在类字段中定义此函数,但也可以在构造函数中使用箭头函数定义此函数.问题不在于此类字段.同样,您使用**curried**函数作为回调处理程序.它被重新创建(不涉及重新渲染).使用curried函数类似于使用箭头函数. (3认同)
  • 同样,以任何一种方式执行此操作取决于编写其应用程序的人员.但是,我在争论另一件事.这里建议的是不正确的.OP使用与否.也许@BhojendraRauniyar想要查看Jordan Harband的回答:https://github.com/airbnb/javascript/issues/659#issuecomment-222519884 (3认同)

Cer*_*nce 8

取而代之的.bind荷兰国际集团或创建一个匿名箭头功能render(),您可以创建绑定/匿名函数之外render(),比如实例化对象,在构造函数中,或类似的东西,并使用参考该奇异(从不重新创建)功能.例如,运行一次:

this.boundHandleClick = this.handleClick.bind(this, 111);
Run Code Online (Sandbox Code Playgroud)

要么

this.boundHandleClick = () => this.handleClick(111);
Run Code Online (Sandbox Code Playgroud)

然后render,参考boundHandleClick:

return (
  <div className="App">
    <button onClick={this.boundHandleClick}>Click</button>
  </div>
);
Run Code Online (Sandbox Code Playgroud)

如果你需要使用的参数(111)render,那么你可以使用对象查找,看是否与参数绑定功能还不存在.如果是这样,只需使用该绑定函数 - 否则,创建它(一次,因此无论何时在将来使用该参数时都不必再次创建):

this.boundClicks = {};
// ...
if (!this.boundClicks['111']) this.boundClicks['111'] = () => this.handleClick(111);
return (
  <div className="App">
    <button onClick={this.boundClicks['111']}>Click</button>
  </div>
);
Run Code Online (Sandbox Code Playgroud)

  • **这是正确的答案!**底线:避免在渲染中声明箭头功能或绑定以获得最佳性能.在`render`之外声明你的函数.每个渲染都不再有功能分配. (2认同)

dev*_*kan 6

这取决于您获取参数的方式.有时您无法轻易避免使用.bind箭头或箭头功能,但大多数情况下您可以以某种方式获取参数.正如您在@ CertainPerformance的答案中所看到的,如果您可以在构造函数中使用此参数,您可以更喜欢这种方式.但是可以有其他方法.

例如,假设您在州中有一个列表..bind您可以将列表元素传递给子组件,然后在那里使用回调处理程序,而不是直接映射此列表并使用箭头函数.

class App extends React.Component {
  state = {
    list: [ "foo", "bar" ],
  };

  handleClick(el) { console.log( el ) }

  render() {
    return (
      <div>
        {this.state.list.map( el => (
          <Child key={el} el={el} onClick={this.handleClick} />
        ) )}
      </div>
    );
  }
}

const Child = ( props ) => {
  const handleClick = () => props.onClick( props.el );
  return (
    <div>
      {props.el}
      <button onClick={handleClick}>Click</button>
    </div>
  );
};

ReactDOM.render( <App />, document.getElementById( "root" ) );
Run Code Online (Sandbox Code Playgroud)
<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="root"></div>
Run Code Online (Sandbox Code Playgroud)

我正在更新我的答案,演示如何使用内联箭头功能,或绑定它,或使用curried函数导致在这里重新创建.

假设您有一个组件,并且此组件具有写为的子组件React.PureComponent.通常,如果这个孩子的道具没有改变,它将不会重新渲染.凉.我们的父组件中有一个类方法,并希望将其作为处理程序传递给子组件.让我们看看这里发生了什么.

首先,我不传递处理程序,当您在父级中递增计数器时,子组件不会再次重新渲染(初始渲染除外).这是因为我们将其定义为PureComponent.除非道具改变,否则我们不希望它被重新渲染.

class App extends React.Component {
  state = {
    counter: 0,
  };

  increment = () =>
    this.setState( currentState => ( {
      counter: currentState.counter + 1,
    } ) );

  handleClick(param) { console.log( param ) }

  render() {
    return (
      <div>
        <button onClick={this.increment}>Increment</button>
        Counter is: {this.state.counter}
        <Child />
      </div>
    );
  }
}

class Child extends React.PureComponent {
  render() {
    console.log( "child rendered" );
    return (
      <div>
        <button onClick={this.props.onClick}>Click</button>
      </div>
    );
  }
}

ReactDOM.render( <App />, document.getElementById( "root" ) );
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

如您所见,子组件未被重新渲染.现在让我们使用我们的类方法,使用内联箭头函数.

class App extends React.Component {
  state = {
    counter: 0,
  };

  increment = () =>
    this.setState( currentState => ( {
      counter: currentState.counter + 1,
    } ) );

  handleClick( param ) { console.log( param ) }

  render() {
    return (
      <div>
        <button onClick={this.increment}>Increment</button>
        Counter is: {this.state.counter}
        <Child onClick={() => this.handleClick( "some param" )} />
      </div>
    );
  }
}

class Child extends React.PureComponent {
  render() {
    console.log( "child rendered" );
    return (
      <div>
        <button onClick={this.props.onClick}>Click</button>
      </div>
    );
  }
}

ReactDOM.render( <App />, document.getElementById( "root" ) );
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

Ooops,当我们递增计数器时,子项被渲染.但是,它与计数器状态没有任何关系,我们不希望这样.那为什么要重新渲染呢?这是因为我们在onClick它得到的道具中使用了内联箭头功能.由于此函数在父级的每个渲染中重新创建,因此其引用更改为不同的函数,并且子级认为它获得了新的支柱!但实际上它并没有得到它.我们可以在我们的处理程序中使用该参数,但是有不必要的渲染

现在随着.bind.我没有this在绑定中使用,因为我们不使用this我们的简单方法.它只记录一个参数.

class App extends React.Component {
  state = {
    counter: 0,
  };

  increment = () =>
    this.setState( currentState => ( {
      counter: currentState.counter + 1,
    } ) );

  handleClick( param ) { console.log( param ) }

  render() {
    return (
      <div>
        <button onClick={this.increment}>Increment</button>
        Counter is: {this.state.counter}
        <Child onClick={this.handleClick.bind( null, "some param" )} />
      </div>
    );
  }
}

class Child extends React.PureComponent {
  render() {
    console.log( "child rendered" );
    return (
      <div>
        <button onClick={this.props.onClick}>Click</button>
      </div>
    );
  }
}

ReactDOM.render( <App />, document.getElementById( "root" ) );
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

在这里,我们可以使用参数,但有不必要的渲染.现在有一个咖喱功能.

class App extends React.Component {
  state = {
    counter: 0,
  };

  increment = () =>
    this.setState( currentState => ( {
      counter: currentState.counter + 1,
    } ) );

  handleClick( param ) { 
    return function() {
      console.log( param ) 
    }
  }

  render() {
    return (
      <div>
        <button onClick={this.increment}>Increment</button>
        Counter is: {this.state.counter}
        <Child onClick={this.handleClick( "some param" )} />
      </div>
    );
  }
}

class Child extends React.PureComponent {
  render() {
    console.log( "child rendered" );
    return (
      <div>
        <button onClick={this.props.onClick}>Click</button>
      </div>
    );
  }
}

ReactDOM.render( <App />, document.getElementById( "root" ) );
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

惊喜!再次不必要的渲染 现在,对于一个组件,这不是那么重要.但是,如果你的应用程序像这个孩子一样有各种各样的组件呢?

现在,让我们假设我以某种方式获得了参数.我在这里用硬编码的字符串模仿它.

class App extends React.Component {
  state = {
    counter: 0,
  };

  increment = () =>
    this.setState( currentState => ( {
      counter: currentState.counter + 1,
    } ) );

  handleClick() { console.log( "some param" ) }

  render() {
    return (
      <div>
        <button onClick={this.increment}>Increment</button>
        Counter is: {this.state.counter}
        <Child onClick={this.handleClick} />
      </div>
    );
  }
}

class Child extends React.PureComponent {
  render() {
    console.log( "child rendered" );
    return (
      <div>
        <button onClick={this.props.onClick}>Click</button>
      </div>
    );
  }
}

ReactDOM.render( <App />, document.getElementById( "root" ) );
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

咄!由于我们使用了函数引用,因此无需按预期进 我可以使用参数,但生活并不那么容易,OP实际上询问我们如何使用参数而不使用内联箭头函数,绑定或使用curried函数.所有的大惊小怪就是这个.

即使我们没有将此处理程序传递给组件,但仍然会在我们在此处看到的父级的每个渲染中重新创建它.如果您有一个项目列表,可以说它们中的500个,并且您将它们映射到父组件中的按钮并使用箭头功能等,这意味着它们将在每个渲染中重新创建(500次)!

所以,没有任何简单的方法可以做到这一点.如果我们的参数不是来自事件对象,那么我们要么使用@ CertainPerformance的解决方案,要么像我在这里一样尝试改变逻辑.