在 React 中处理事件的新方法

*“Property Initializer Syntax”* 听起来比实际更花哨。在这个简短的教程中,了解这种编写事件处理程序的替代方法将如何帮助消除您的样板文件,constructor并防止渲染中的琐碎内存使用。

在 Facebook 文档中,你会看到事件处理是这样完成的:

// option 1
class Thing extends React.Component {
  constructor() {
    this.handleSmthng = this.handleSmthng.bind(this)
  }
  render() {
    <input onChange={this.handleSmthng}/>
  }
  handleSmthng(e) {
    // ...
  }
}

ES6 类不会自动赋予this作用域handleSmthng,并且由于您通常想要调用this.setState或可能调用组件中的另一个方法,“官方”约定是始终在构造函数中绑定所有事件处理程序这有效,但很快就会感觉像样板代码。


// option 2
class Thing extends React.Component {
  render() {
    <button onClick={() => this.handleSmthng('foo')}>
      ADD
    </button>
  }
  handleSmthng(arg1) {
    // ...
  }
}

这种模式似乎在网上的 React 教程中越来越流行。它将this上下文传递handleSmthng并避免构造函数中的样板代码。哎呀,这个组件中没有状态,所以你甚至不需要构造函数!我认为这种方法的动机是正确的……但有一点性能成本。

使用箭头函数总是会在 JavaScript 中创建一个新的引用,这反过来会增加应用程序的内存使用量。虽然 JavaScript 中的内存很便宜,但 React 中的渲染成本很高。当您将箭头函数传递给子组件时,您的子组件将不加选择地重新渲染,因为(就它而言)箭头函数是新数据。这可能意味着大型 React 应用程序获得 60fps 或 50fps 之间的差异。

“……但是,如果此回调作为道具传递给较低的组件,则这些组件可能会进行额外的重新渲染。” 反应文档

轻轻眨眼

两个绑定一个关闭

有一种更简洁的方法来编写事件处理程序,1) 避免样板和 2) 不会导致额外的重新渲染:属性初始值设定项语法这是一个花哨的名字,但这个想法真的很简单……只需使用箭头函数来定义您的事件处理程序。像这样:

class TodoApp extends React.Component {
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          ADD
        </button>
        <input onChange={this.handleInput}/>
      </div>
    );
  }
  handleClick = () => {
    // "this"
  }
  handleInput = (e) => {
    // "this", "e"
  }
}

你定义了两个处理程序,它看起来非常好。没有样板。易于阅读。易于重构……如果您想传递参数:

class TodoApp extends React.Component {
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          ADD
        </button>
        <input onChange={this.handleInput}/>
        {
          this.state.todos.map((item) => {
            return (
              <li onClick={this.handleRemove(item.id)}>
                {item.text}
              </li>
            );
          });
        }
      </div>
    )
  }

  handleClick = () => {
    // "this"
  }

  handleInput = (e) => {
    // "this", "e"
  }

  handleRemove = (id) => (e) => {
    // "this", "e", "id"
  }
}

繁荣!您可以bind不使用渲染方法或构造函数的情况下传递参数一切看起来都非常整洁。

如果您不习惯看到箭头函数,这可能看起来很奇怪。请记住,在 ES6 中,粗箭头语法允许在一行语句中省略大括号……它会隐式地包含return该行上的任何内容!这是 Babel 的转译方式handleRemove

handleRemove: function (item) {
  return function (e) {
    // "item" AND "e" 🌈
  }
}

要将 Babel 配置为使用 Property Initializer Syntax,请确保您已安装“transform-class-properties”插件或启用 stage-2如果您正在使用“create-react-app”……它已经在那里了!

有一个ESLint 规则会告诉你不要在渲染中使用“绑定”或箭头函数

使用属性初始值设定项语法的好处

我最好的猜测是 Facebook 没有在他们的文档中“正式”认可这种模式是因为第 2 阶段 ES6 还没有最终确定,所以属性初始化器仍然被认为是非标准的。然而,create-react-app生成器已经启用了第 2 阶段,所以……很可能在不久的将来,属性初始化器将成为定义事件处理程序的事实。

一旦您熟悉了 Property Initializers 并开始使用它们来定义处理程序方法,您将获得两个显着的好处:

  • 编写更少的样板不必bind在构造函数中编写语句是非常甜蜜的。现在你只需定义方法——就是这样 ✨。如果您需要传递参数,只需用一个闭包包装并记住在render. 作为一个额外的好处,如果您需要在其他地方重构您的事件处理程序,您只能从一个地方进行剪切粘贴。
  • 降低内存使用量使用箭头函数render是一个坏主意,因为“按设计”渲染在组件的生命周期中会大量发生;为每个箭头函数分配一个新指针。放弃箭头函数 inrender将确保您保持组件的内存使用量。

查看此CodePen以查看操作中的属性初始值设定项语法

觉得文章有用?

点个广告表达一下你的爱意吧 !😁