我应该在哪里绑定React组件中的方法?

问题描述 投票:2回答:6

我现在学习React并注意到很多人在构造函数中绑定他们的方法。

像这样:

class MyComponent extends React.Component {
  constructor() {
    super();
    this.myMethod = this.myMethod.bind(this);
  }

  render() {
    <button onClick={this.myMethod}>Click me</button>
  }

  myMethod() {
    // do something
  }
}

但我习惯写这样的东西:

render() {
    <button onClick={this.myMethod.bind(this)}>Click me</button>
}

有几个人告诉我,使用第二种方法是一种糟糕的体验。

那么你能告诉我第一种和第二种方法之间的区别吗?有什么优点和缺点?或性能问题?

javascript reactjs ecmascript-6 arrow-functions
6个回答
1
投票

你是对的,别人告诉你的也是对的。

我们鼓励你在构造函数中进行绑定,因为构造函数每个组件只被调用一次,所以如果你在构造函数中进行绑定,它只会在Webpack bundle.js文件中创建一个新的对象/函数,因此影响不大

我们不鼓励您直接在渲染中进行绑定,因为组件渲染有几个原因,例如当您执行setState时,当组件接收到新的道具时,您的组件将渲染这么多次。因此,每当您的组件呈现时,您都会直接在渲染中绑定它,它将在Webpack bundle.js中每次创建一个新函数,并且您的包文件大小将增加,并且当您的应用程序包含数千个组件并且您直接绑定时会影响性能在每个组件中渲染。

因此建议您仅在构造函数中进行绑定。希望澄清一下


1
投票

您应该在构造函数中绑定,因为第二种方法将在每次渲染时创建一个新函数。

但是有一种更好的方法可以简单地避免绑定。使用箭头功能。

class MyComponent extends React.Component {
  constructor() {
    super();
  }

  render() {
    <button onClick={this.myMethod}>Click me</button>
  }

  myMethod = ()=> {
    // do something
  }
}

让我们来看看Redux Dan Abramov thinks about bind vs arrow functions的创造者 -

Question

在性能方面,使用箭头函数和使用es6类时手动绑定有什么区别吗?使用箭头函数的方法不在类原型上,它只在类实例上。使用bind会将方法附加到类原型。听起来像手动绑定会有更好的性能,这是否意味着我们应该考虑使用bind而不是箭头函数用于类方法?

任何建议或意见真的很感激!

所以在性能方面,你会建议使用吗?

class MyComponent扩展了React.Component {constructor(props){super(props)}

methodA =()=> {...}}

要么

class MyComponent扩展了React.Component {constructor(props){super(props)this.methodA = this.methodA.bind(this)}

methodA(){...}}

Answer

这两种写作方式是等价的。 (第二个编译为第一个。)

使用bind会将方法附加到类原型。

在您的示例中,您仍然将该函数附加到实例:

this.methodA = this.methodA.bind(this)

所以他们基本上是一样的。

在Facebook,我们使用第二种方式(“类属性”)但要注意这仍然是实验性的,而不是ES6的一部分。如果您只想坚持使用稳定的语法,那么您可以手动绑定它们。


1
投票

这导致在每个render调用上创建一个新的绑定函数:

render() {
    <button onClick={this.myMethod.bind(this)}>Click me</button>
}

请注意,如果在多个地方使用myMethod,则需要多次bind调用,并且如果缺少bind之一,则可能导致未绑定的回调。

虽然这会在组件实例化上创建绑定函数:

  constructor() {
    super();
    this.myMethod = this.myMethod.bind(this);
  }

建议使用第二种方法。

autobind这样的装饰器可用于在构造函数中跳过myMethod显式赋值。

正如在this answer中所解释的那样,使用bind的原型方法比箭头实例方法具有更少的缺点,并且通常是优选的。


0
投票

我从eslint-plugin-react文档中获取了这个:

JSX prop中的绑定调用或箭头函数将在每个渲染上创建一个全新的函数。这对性能不利,因为它会导致垃圾收集器的调用方式超出必要的范围。如果将全新的函数作为prop传递给使用对prop的引用相等性检查以确定是否应该更新的组件,它也可能导致不必要的重新呈现。

作为我的旁注,在JSX中使用this也会让人感到困惑。我鼓励你看看这个文档:https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md


0
投票

你应该避免使用箭头函数并在渲染中绑定。它打破了像shouldComponentUpdate和PureComponent这样的性能优化。

有关精彩的阅读和演示,您可能需要参考this.


0
投票

第一种方法是正确的性能,因为在每个渲染中,这个onClick道具将指向同一个对象,而在第二个例子中并非如此。


如果你看下面的例子,你会看到我增加计数器时,MyPureCompOne不会渲染,但MyPureCompTwo会。因为每次<App>组件呈现时,MyPureCompTwo都会为handleClick赋予一个新的函数对象,这就是为什么作为纯组件的道具的浅比较是错误的并且它呈现。不需要这种渲染。但MyPureCompOne的情况并非如此,因为每次App渲染时,handleClick道具仍然指向同一个功能对象(this.handleClickOne),这是在App第一次安装时创建的。

class MyPureCompOne extends React.PureComponent {
  render() {
    console.log("rendring component one");
    return <button onClick={this.props.handleClick}>First Button</button>
  }
}

class MyPureCompTwo extends React.PureComponent {
  render() {
    console.log("rendring component two");
    return <button onClick={this.props.handleClick}>Second Button</button>;
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
    this.handleCountChange = this.handleCountChange.bind(this);
    this.handleClickOne = this.handleClickOne.bind(this);
  }

  handleCountChange() {
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  }

  handleClickOne(e) {
    console.log("Clicked..");
  }

  handleClickTwo() {
    console.log("Clicked..");
  }

  render() {
    const { count } = this.state;
    return (
      <div>
        <button onClick={this.handleCountChange}>Change Counter</button>
        <MyPureCompOne handleClick={this.handleClickOne} />;
        <MyPureCompTwo handleClick={this.handleClickTwo.bind(this)} />
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>


<div id='root'></div>
© www.soinside.com 2019 - 2024. All rights reserved.