一种在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” 插件或 enable stage-2。 如果你正在使用“create-react-app”……它已经存在了!
有一个 ESLint 规则 会告诉你不要在渲染中使用“绑定”或箭头函数
使用属性初始化器语法的好处
我最好的猜测是 Facebook 没有在他们的文档中“正式”认可这种模式是因为第二阶段 ES6 还没有最终确定,所以 Property Initializers 仍然被认为是非标准的。 然而,create-react-app 生成器已经启用了第 2 阶段,所以……很可能在不久的将来,Property Initializers 将成为定义事件处理程序的事实。
一旦您熟悉了 Property Initializers 并开始使用它们来定义处理程序方法,您将获得两个显着的好处:
- 编写更少的样板 不必在构造函数中编写
bind语句是相当不错的。 现在你只需定义方法——就是这样✨。 如果您需要传递参数,只需使用单个闭包进行包装,并记住在render中调用该处理函数。 作为一个额外的好处,如果您需要在其他地方重构您的事件处理程序,您只有一个可以剪切粘贴的地方。 - 降低内存使用量 在
render中使用箭头函数是一个坏主意,因为“设计”渲染在组件的生命周期中会大量发生; 为每个箭头函数分配一个新指针。 弃权render中的箭头函数将确保您在节食时保持组件的内存使用。
查看此 CodePen 以查看 Property Initializer Syntax 的实际应用