React 中箭头函数的正确使用

IT技术 javascript reactjs arrow-functions ecmascript-next class-fields
2021-01-14 22:43:41

我将 ReactJS 与 Babel 和 Webpack 一起使用,并使用 ES6 以及建议的箭头函数类字段我知道箭头函数通过不重新创建每个渲染的函数来提高效率,类似于构造函数中的绑定工作方式。但是,我不能 100% 确定我是否正确使用它们。以下是我在三个不同文件中的代码的简化部分。

我的代码:

主程序

prevItem = () => {
    console.log("Div is clicked")
}

render(){
    return (
         <SecondClass prevItem={this.prevItem} />
    )
}

第二类.js

<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />

第三类.js

<div onClick={()=>{this.props.onClick()}}>Previous</div>

问题:

我上面的代码是否正确使用了箭头函数?我注意到对于 SecondClass.js 我也可以使用:

<ThirdClass type="prev" onClick={this.props.prevItem} />

因为我在原始函数定义中使用了 ES6 箭头函数,所以一种方法或另一种方法之间有区别吗?或者我应该一直使用箭头语法直到我的最后一个 div?

5个回答

我知道箭头函数通过不在每次引用时重新创建函数来提高效率

不是真的

箭头函数this以词法方式处理上下文,“普通”函数动态处理如果您需要有关它的更多信息,我会深入介绍这个关键字

在您的两个内联箭头函数示例中,您都在每个render.
这将在每个渲染上创建并传递一个新实例

onClick={() => {}}

在第三个示例中,您只有一个实例。
这仅传递对已存在实例的引用

onClick={this.myHandler}


至于箭头函数作为类字段的好处(有一个小的缺点,我会将其发布在答案的底部),如果您有一个需要访问classvia当前实例的普通函数处理程序this

myHandler(){
  //  this.setState(...)
}

您需要将其显式显示bindclass.
最常见的方法是在 中执行它,constructor因为它只运行一次:

constructor(props){
  super(props);
  this.myHandler = this.myHandler.bind(this);
}

如果您使用箭头函数作为处理程序,则不需要bind它,class因为如上所述,箭头函数使用词法上下文this

myHandler = () => {
  //  this.setState(...)
}

通过这两种方法,您将像这样使用处理程序:

<div onClick={this.myHandler}></div> 

采取这种方法的主要原因:

<div onClick={() => this.myHandler(someParameter)}></div>

是如果您想将参数传递给传递的本机旁边的处理程序event,这意味着您要向上传递参数。

如前所述,这将在每个渲染上创建一个新的函数实例。
(对此有更好的方法,请继续阅读)。

此类用例的运行示例:

更好的方法是创建组件组合。
您可以创建一个包装相关标记的子组件,将拥有它自己的处理程序,datahandler从父组件中获取 theas props。

然后子组件将调用它从父组件获取的处理程序并将其data作为参数传递

使用子组件运行示例:

类字段的缺点
正如我所提到的,类字段有一个小的缺点。
一个类的方法和一类字段之间的区别是,类字段被附接到instance所述的class(构造函数)。
其中类方法和对象附加到原型。

因此,如果您将拥有大量此类的实例,则可能会导致性能下降。

鉴于此代码块:

class MyClass {
  myMethod(){}  
  myOtherMethod = () => {}
}

babel 会将其转译为:

var _createClass = function() {
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }
  return function(Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
}();

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var MyClass = function() {
  function MyClass() {
    _classCallCheck(this, MyClass);

    this.myOtherMethod = function() {};
  }

  _createClass(MyClass, [{
    key: "myMethod",
    value: function myMethod() {}
  }]);

  return MyClass;
}();

我知道箭头函数通过不重新创建每个渲染的函数来提高效率,这类似于构造函数中的绑定工作方式。

这不是真的。这取决于您使用箭头函数的确切位置。如果Arrow function在 render 方法中使用,那么它们创建一个新的实例everytimerender 就像如何bind工作一样被调用考虑这个例子

<div onClick={()=>{this.onClick()}}>Previous</div>

这里每次调用 render 时都会创建一个匿名函数,并且该函数在调用时调用this.onClick.

但是考虑下面的情况

onClick = () => {
    console.log("Div is clicked")
}

在上述情况下,箭头函数不会每次都重新创建函数,而是在An arrow function does not have its own this; the this value of the enclosing execution context is used. 实例化类时将上下文绑定到 React 组件这类似于如何binding works is constructor这是proposed class fields for arrow functionsES6 特性的一部分,不是 ES6 特性,

要了解您想询问什么,您必须知道一个函数从它被调用的地方获取它的上下文。检查this question以获取更多理解。

在您的情况下,您曾经Arrow function定义过prevItem,因此它获得了封闭的 React 组件的上下文。

prevItem = () => {
    console.log("Div is clicked")
}

render(){
    return (
         <SecondClass prevItem={this.prevItem} />
    )
}

现在,在它的孩子,即使你调用prevItem任何自定义的背景下,using bind or arrow functionprevItem当父执行,即Main.js会得到其封闭的情况下作出react的组成部分。并且由于您只想执行 prevItem 函数并且不想将任何数据从孩子传递给 this,因此编写

<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />

<div onClick={()=>{this.props.onClick()}}>Previous</div>

根本没用而且由于在创建新的功能只会增加性能含义SecondClassThirdClass每次。您根本不需要将这些函数定义为箭头函数,只需编写

<ThirdClass type="prev" onClick={this.props.prevItem} />

<div onClick={this.props.onClick}>Previous</div>

因为它已经绑定在父级中。

现在,即使您必须将一些额外的数据从 ThirdClass 和 SecondClass 传递给这些函数,您也不应该直接使用Arrow functionbind in render看看这个答案How to Avoid binding in Render method

@DivyanshuMaithani 上面的代码是否也适用于功能组件或仅适用于类组件,在使用功能组件 SecondClass.js <ThirdClass type="prev" onClick={()=>this 时,我们是否需要像下面那样再次绑定它.props.prevItem()} /> ThirdClass.js <div onClick={()=>{this.props.onClick()}}>Previous</div>
2021-03-22 22:43:41
我的评论是关于性能问题。答案有点混乱,总之你应该只需要绑定一个类方法(在类组件中)。无需为功能组件绑定函数,因为您不会关心使用this情况。
2021-03-25 22:43:41
使用箭头函数本身并不坏。render即使重新创建箭头函数,可以使用它们。在大多数应用程序中,性能差异不会很明显。
2021-04-03 22:43:41

所以你的第一个方法

<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />

在这种情况下,您可以将 ThirdClass 中可用的任何参数传递给 prevItem 函数。这是使用参数调用父函数的好方法。像这样

<ThirdClass type="prev" onClick={()=>this.props.prevItem(firstArgument, secondArgument)} />

你的第二种方法是

<ThirdClass type="prev" onClick={this.props.prevItem} />

这种方法不允许您传递任何特定于 ThirdClass 的参数。

这两种方法都是正确的,仅此而已,这取决于您的用例。使用 es6 箭头函数的两种方法都适用于上述各个场景

使用JavaScriptcurring函数声明,可以是其他答案的不同方式,注意以下代码:

clickHandler = someData => e => this.setState({
  stateKey: someData
});

现在JSX,你可以写:

<div onClick={this.clickHandler('someData')} />

clickHandlersomeData带有返回的函数e的参数,但它没有内部使用clickHandler功能。所以效果很好。

为了写得更完整,写如下:

clickHandler = someData => () => this.setState({
  stateKey: someData
});

没有必要e,所以我为什么要写它。

在原始函数定义中使用箭头允许您不在构造函数中绑定函数。

如果你没有使用箭头...

prevItem(){
  console.log("Div is clicked")
}

然后你必须创建一个构造函数并将它绑定在那里......

class MyComponent extends Component {
  constructor(props) {
    super(props)
    this.prevItem = this.prevItem.bind(this)
  }

  prevItem() { ... }
}

开始时使用箭头会更容易,因为它可以正常工作,而且您不必了解构造函数是什么,也不必深入研究thisjavascript 中的复杂性

但是,性能方面最好绑定在构造函数中。构造函数方法中的绑定将创建函数的单个实例并重新使用它,即使多次调用渲染方法也是如此。

使用这种方法,有没有办法在调用 prevItem 时添加参数。例如:onpress={this.prevItem}但我喜欢打电话this.prevItem(variable)
2021-03-28 22:43:41