React中箭头函数的正确使用

新手上路,请多包涵

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

我的代码:

主.js

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

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

SecondClass.js

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

ThirdClass.js

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

问题:

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

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

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

原文由 kojow7 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 956
2 个回答

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

这不是真的。这取决于您使用箭头功能的确切位置。如果 Arrow function 在渲染方法中使用,那么它们将创建一个新实例 everytime 渲染就像 bind 一样被调用。考虑这个例子

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

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

但是考虑下面的情况

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

在上面的例子中,箭头函数并不是每次都重新创建函数,而是在实例化类时将上下文绑定到 React 组件,如 An arrow function does not have its own this; the this value of the enclosing execution context is used. 一次。这类似于 binding works is constructor 的方式。这是 proposed class fields for arrow functions 的一部分,它不是 ES6 功能,

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

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

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

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

Now in its child, even if you call prevItem with any custom context, using bind or arrow function , prevItem when executed in parent ie Main.js will get其封闭的 React 组件的上下文。并且由于您只想执行 prevItem 函数并且不想将任何数据从孩子传递给它,所以写

<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

原文由 Shubham Khatri 发布,翻译遵循 CC BY-SA 3.0 许可协议

我知道箭头函数通过在每次引用它们时不重新创建函数来使事情更有效率

不是 真的

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

在您的两个内联箭头函数示例中,您都在每个 render 上创建了一个新的函数实例。

这将在每个渲染器上创建并传递一个新实例

onClick={() => {}}

在第三个示例中,您只有一个实例。

这仅传递对已存在实例的引用

onClick={this.myHandler}


至于箭头函数作为类字段的好处(有一个 的缺点,我会把它贴在答案的底部),如果你有一个普通的函数处理程序需要访问 class 的当前实例 --- 通过 this

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

您需要将 bind 显式显示为 class

最常见的方法是在 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 被传递,这意味着你想向上传递一个参数。

如前所述,这将在每个渲染器上创建一个新的函数实例。

(对此有更好的方法,请继续阅读)。

此类用例的运行示例:

 class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      items: [{ name: 'item 1', active: false }, { name: 'item 2', active: true }],
    }
  }
  toggleITem = (itemName) => {
    this.setState(prev => {
      const nextState = prev.items.map(item => {
        if (item.name !== itemName) return item;
        return {
          ...item,
          active: !item.active
        }
      });
      return { items: nextState };
    });
  }
  render() {
    const { items } = this.state;
    return (
      <div>
        {
          items.map(item => {
            const style = { color: item.active ? 'green' : 'red' };
            return (
              <div
                onClick={() => this.toggleITem(item.name)}
                style={style}
              >
                {item.name}
              </div>

          )})
        }
      </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>

更好的方法是创建组件组合。

您可以创建一个包装相关标记的子组件,它将拥有自己的处理程序,并将同时获得 datahandler 作为来自父级的道具。

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

使用子组件运行示例:

 class Item extends React.Component {
  onClick = () => {
    const { onClick, name } = this.props;
    onClick(name);
  }
  render() {
    const { name, active } = this.props;
    const style = { color: active ? 'green' : 'red' };
    return (<div style={style} onClick={this.onClick}>{name}</div>)
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      items: [{ name: 'item 1', active: false }, { name: 'item 2', active: true }],
    }
  }
  toggleITem = (itemName) => {
    this.setState(prev => {
      const nextState = prev.items.map(item => {
        if (item.name !== itemName) return item;
        return {
          ...item,
          active: !item.active
        }
      });
      return { items: nextState };
    });
  }
  render() {
    const { items } = this.state;
    return (
      <div>
        {
          items.map(item => {
            return <Item {...item} onClick={this.toggleITem} />
          })
        }
      </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>

类字段的 缺点

正如我所提到的,类字段有一个小缺点。

类方法和类字段的区别在于类字段附加到 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;
}();

原文由 Sagiv b.g 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题