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

IT技术 javascript reactjs
2021-03-07 00:31:48

当事件处理程序使用时this(juethandleClick如下使用this.setState),您必须使用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>
    );
}

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

  1. bind(this, param) 函数后
  2. 匿名箭头函数

IE

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

这是问题。

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

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

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

使用匿名箭头函数也是如此。医生说...

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

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

任何输入将不胜感激。

聚苯乙烯

有人问param怎么确定的。这将是动态确定的(即并非总是如此111)。因此,它可以来自状态、props或此类中定义的其他一些函数。

4个回答

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

this.boundHandleClick = this.handleClick.bind(this, 111);

或者

this.boundHandleClick = () => this.handleClick(111);

然后,在render,参考boundHandleClick

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

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

this.boundClicks = {};
// ...
if (!this.boundClicks['111']) this.boundClicks['111'] = () => this.handleClick(111);
return (
  <div className="App">
    <button onClick={this.boundClicks['111']}>Click</button>
  </div>
);
我在这里同意@SteveArcher。大多数时候做这样的事情不是很困难吗?参数通常是动态的,而且很多。
2021-04-16 00:31:48
准确,但在大多数用例中,我已经看到当您在构造函数中进行绑定时,您还没有想要传递的值。很多时候,您传递的内容是在渲染发生之前未计算的内容,或者是来自上下文提供程序的值之类的内容。很多时候不确定在渲染中是否有任何方法可以绑定。
2021-04-27 00:31:48
是的,我的回答是针对已删除的评论,而不是针对您的 :) 在那个人中,有人说问题是关于将绑定与箭头函数进行比较。
2021-05-03 00:31:48
@devserkan OP 担心每次render()发生时创建新函数的性能影响——这是一种解决方法,它只在必要时创建函数,而不是每次都创建。
2021-05-05 00:31:48
这是正确的答案!底线:避免在渲染中声明箭头函数或绑定以获得最佳性能。render. 每次渲染时不再分配功能。
2021-05-08 00:31:48

我已经在我的另一篇文章中解释了它:react组件中的点击事件

如果您担心其性能,请不要使用内联箭头函数。您仍然可以使用公共类方法并绑定上下文this

handleClick = () => () => {
    this.setState({ // this works now
      isClicked:true
    });
}

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

handleClick = (param1, param2, param3) => (event) => {

根据devserkan的评论,

这是柯里化的,与其他选项相同。这个函数也会在每次渲染中重新创建。

不,它没有。请参阅文档中的注释

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

另外,请参阅某些性能的答案下方来自bigga-hd的评论

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


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

您可以像这样调用该方法:

onClick={this.handleClick(param1,param2,param3)}

PS:我没有将此帖子标记为重复,因为问题范围有显着差异。因此,只需链接该帖子即可让您深入了解更多细节。

我可以看到您已经更新了答案,但这并没有回答我的问题。文档没有说任何关于柯里化的内容。如果我没记错的话,这就是柯里化。因此,您在回调处理程序中使用了柯里化函数,并且在每次渲染中都会重新创建它。
2021-04-15 00:31:48
这是柯里化的,与其他选项相同。这个函数也会在每次渲染中重新创建。
2021-04-22 00:31:48
也许你是误解它的人:) 公共类字段建议只是绑定的一种替代方法,仅此而已。它与重新渲染无关。您可以不在类字段中定义此函数,也可以在构造函数中使用箭头函数定义此函数。问题不在于这里的类字段。同样,您使用柯里化函数作为回调处理程序。它被重新创建(与重新渲染无关)。使用柯里化函数类似于使用箭头函数。
2021-04-29 00:31:48
好吧,让我们停下来。但看到这一点,很难过。建议在箭头一或绑定上使用柯里化函数。这是同样的事情。在文档中,他们不使用柯里化函数,这就是它起作用的原因。每当你()在回调中看到你使用的时候,这意味着回调被重新创建,因为它是重新创建的组件,这些组件接受这个认为是一个改变的道具并且它们被重新渲染。
2021-05-07 00:31:48
同样,以任何一种方式执行此操作取决于编写应用程序的人。但是,我在争论另一件事。这里所建议的不是真的。OP使用与否。也许@BhojendraRauniyar 想查看 Jordan Harband 的回答:github.com/airbnb/javascript/issues/659#issuecomment-222519884
2021-05-14 00:31:48

这取决于您如何获取参数。有时您无法.bind轻松避免使用 a或箭头函数,但大多数时候您可以以某种方式获取参数。正如您在@CertainPerformance 的回答中看到的那样,如果您可以在构造函数中使用此参数,您可以更喜欢这种方式。但是可以有其他方法。

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

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" ) );
<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>

我正在通过演示更新我的答案,如何使用内联箭头函数,或绑定它,或使用柯里化函数会导致这里的娱乐。

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

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

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" ) );
<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>

如您所见,子组件没有重新渲染。现在让我们用我们的类方法来做到这一点,使用内联箭头函数。

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" ) );
<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>

oop,当我们增加计数器时,就会呈现孩子。但是,它与计数器状态没有任何关系,我们不想要这个。那么为什么要重新渲染呢?这是因为我们在onClick它获得props中使用了内联箭头函数由于此函数在父级的每次渲染中重新创建,因此它的引用更改为不同的函数,子级认为它获得了一个新的props!但实际上它并没有得到它。我们可以将参数与我们的处理程序一起使用,但有不必要的渲染。

现在随着.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" ) );
<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>

同样在这里,我们可以使用参数,但没有不必要的渲染。现在有一个咖喱函数。

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" ) );
<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>

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

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

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" ) );
<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>

呸!由于我们使用了函数引用,因此没有按预期进行不必要的重新渲染。我可以使用参数,但生活并不那么容易,OP 实际上询问我们如何在不使用内联箭头函数、绑定或柯里化函数的情况下使用参数。所有的大惊小怪都是关于这个的。

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

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

2021-04-24 00:31:48
如果您不访问this.
2021-04-26 00:31:48
当然 :) 我的答案取决于它 :D 像我一样提取组件或您在此处提供的旧答案。
2021-05-01 00:31:48
嘿嘿,给我们找了个东西,2015年的一个问题,三年前我们已经有了答案:stackoverflow.com/questions/29810914/...
2021-05-07 00:31:48
你好。正如你所看到的,所有的建议都是相似的,他们提到了重新创建问题(那些用于内联箭头函数、绑定甚至有一个柯里化的问题!)。最可能的解决方案是像我在这里一样提取组件:) 但是,这里@CertainPerformance 的答案是一个加分项。
2021-05-08 00:31:48

我曾经为州名做这种事情。取决于你想要什么,你可以试试这个。这样,我不必再次绑定该函数。您可以从哪里获得州名e.target.name

class App extends React.Component {
  state = {
    change: ""
  };

  handleChange = e => {
    const { name, value } = e.target;
    this.setState({ [name]: value  })
  };

  render() {
    return (
      <input
        type="text"
        onChange={this.handleChange}
        name="change"
        value={this.state.change}
      />
    );
  }
}