React:哪个是推荐的箭头或正常功能?

IT技术 reactjs ecmascript-6 ecmascript-5
2021-03-29 01:28:24

在我觉得手动函数/对象绑定和作用域相关问题令人头疼之后,我开始使用箭头函数,但最近我开始知道使用普通函数(ES5)比使用箭头函数(ES6)更好。

我对这些功能的理解

React 中的普通函数:

  1. 手动绑定对象/函数,以便在函数内部使用 state 或 props 并避免与作用域相关的问题
  2. 始终在构造函数中绑定对象/函数,但不直接在渲染中绑定
  3. 如果你在构造函数中这样做,那么当你的组件第一次渲染时,Webpack 只会在 bundle.js 文件中创建一次新的对象/函数
  4. 如果你直接在渲染中执行,那么每次你的组件渲染和重新渲染时,Webpack 都会在 bundle.js 文件中创建一个新的对象/函数
  5. 如果不绑定,则无法访问状态或props。您必须将当前对象分配给局部变量,否则 this.state 或 this.props 未定义

React 中的箭头函数:

  1. 无需在构造函数中绑定对象/函数,也无需渲染
  2. 您无需依赖当前对象的局部变量,即让 that = this;
  3. 你不会有范围问题,对象/函数绑定会自动执行

但我的疑问是,我听说建议使用普通函数并将其绑定在构造函数中,而不是使用箭头函数,因为每次组件渲染和重新渲染时,箭头函数都会在 Webpack bundle.js 中创建新的对象/函数。

这是真的?推荐哪个?

这个线程接受了答案在 React 中正确使用箭头函数说->这取决于你究竟在哪里使用箭头函数。如果在 render 方法中使用 Arrow 函数,那么它们每次调用 render 时都会创建一个新实例,就像 bind 的工作方式一样。

对不起,如果你觉得这是一个戏剧性的问题,但这是我最大的疑问。请建议

2个回答

那里有很多答案,但人们总是感到困惑。我知道这一点是因为我不久前感到困惑。一段时间后,我掌握了这些概念。

  1. 手动绑定对象/函数,以便在函数内部使用 state 或 props 并避免与作用域相关的问题

不完全正确。您不需要绑定函数来使用 state 或 props。this当您this在范围内丢失上下文时,您可以将函数绑定到例如在回调函数中。

class App extends React.Component {
  state = {
    name: "foo",
  }
  aFunction() {
    console.log( this.state.name );
  }
  render() {
    return <div>{this.aFunction()}</div>;
  }
}

你不需要绑定你的函数,因为this你指向你的类并且你不会丢失它的上下文。但是如果你在回调中像按钮一样使用你的函数,你必须绑定它:

class App extends React.Component {
  state = {
    name: "foo",
  }
  aFunction() {
    console.log( this.state.name );
  }

  render() {
    return (
      <div>
        <button onClick={this.aFunction}>Click</button>
      </div>
    );
  }
}

这不起作用,因为您失去了上下文。现在,您需要以某种方式恢复其上下文,对吗?好的,让我们看看如何做到这一点。首先,我想在按钮回调中绑定它。

<button onClick={this.aFunction.bind(this)}>Click</button>

是的,这有效。但是,它将在每次渲染中重新创建。所以:

  1. 始终在构造函数中绑定对象/函数,但不直接在渲染中绑定

是的。不要像我上面那样绑定它,在你的构造函数中进行。

  1. 如果你在构造函数中这样做,那么当你的组件第一次渲染时,Webpack 只会在 bundle.js 文件中创建一次新的对象/函数

  2. 如果你直接在渲染中执行,那么每次你的组件渲染和重新渲染时,Webpack 都会在 bundle.js 文件中创建一个新的对象/函数

你在这里总结了我迄今为止试图解释的内容。但是,我认为 Webpack 不是这样做的,您的应用程序是。

  1. 如果不绑定,则无法访问状态或props。您必须将当前对象分配给局部变量,否则 this.state 或 this.props 未定义

同样,如果您在类范围内使用您的函数,则不必绑定它。如果你在你的类之外使用这个函数,比如按钮回调,你必须绑定它。这与state无关props这与使用this.

绑定的第二个选项是使用常规函数在构造函数中进行绑定,第三个选项是使用没有绑定的箭头函数。

现在,箭头功能。

1.不需要在构造函数中绑定对象/函数也不需要渲染

是的。

  1. 您无需依赖当前对象的局部变量,即让 that = this;

是的。

  1. 你不会有范围问题,对象/函数绑定会自动执行

是的。

但我的疑问是,我听说建议使用普通函数并将其绑定在构造函数中,而不是使用箭头函数,因为每次组件渲染和重新渲染时,箭头函数都会在 Webpack bundle.js 中创建新的对象/函数。

就像大家说的,这取决于你在哪里使用它们。

render() {
    return (
        <div>
            <button onClick={() => this.aFunction()}>Click</button>
        </div>
    );
}

在这里,它将在每次渲染中重新创建。但是,如果您不需要向它传递任何参数,则可以通过引用使用它。

render() {
    return (
        <div>
            <button onClick={this.aFunction}>Click</button>
        </div>
    );
}

这与上一个相同。因此,如果您()在渲染方法中看到 a ,则该函数会在每次渲染中重新创建。常规或箭头一个,都没有关系。如果您以某种方式调用它,那么您就是在重新创建它。这适用于渲染中的绑定,如aFunction.bind(this)我看到()那里。

因此,通过引用使用函数可以避免此问题。现在,最大的问题是当我们需要一些参数时会发生什么?如果您使用箭头函数传递参数,请尝试更改您的逻辑。

但这真的那么重要吗?就像@Eric Kim 所说的那样,如果你真的需要优化,优化是一个问题。这是一个普遍的建议,因为我从很多人那里听到了这个建议。但就我个人而言,如果函数会在每次渲染中重新创建,我会尽量避免使用它们。但同样,这完全是个人的。

你怎么能改变你的逻辑?您正在使用项目映射数组并创建按钮。在此按钮中,您使用的是将项目名称传递给函数的函数。

{
    items.map( item =>
        <button onClick={() => this.aFunction(item.name)}>Click</button>
    )
}

此功能将在每个项目的每次渲染中重新创建!所以,改变你的逻辑,创建一个单独的Item组件并映射它。通过item,aFunction作为props。然后在此组件中使用处理程序函数使用您的函数。

const Item = ( props ) => {
    const handleClick = () => props.aFunction( props.item.name );
    return (
        <button onClick={handleClick}>Click</button>
    );
}

在这里,您使用的是onClick带有引用处理程序,它会调用您的真实函数。不会在每次渲染中重新创建任何功能。但是,不利的一面是,您需要编写一个单独的组件和更多的代码。

大多数情况下,您可以应用此逻辑。也许会有一些你做不到的例子,谁知道呢。所以决定权在你。

顺便说一下,@widged 在评论中给出的 Medium 帖子是关于这个问题的著名讨论。箭头函数真的比普通函数慢吗?是的。但是多少钱?我猜没有那么多。此外,对于转译的代码也是如此。将来当他们成为本地人时,他们将成为更快的人。

作为个人的旁注。自从我喜欢它们以来,我一直在使用箭头函数。但是前段时间在讨论中,有人说

当我在类中看到一个箭头函数时,我认为:“这个函数正在这个类之外被使用/调用”。如果我看到一个普通的,我就会明白这个函数是在类内部调用的。

我真的很喜欢这种方法,现在如果我不需要在课外调用我的函数,我会使用常规方法。

您在这篇文章中非常随意地使用了“范围”这个词。我认为您正在寻找的是“上下文”。当您专门this在生命周期方法或其他绑定方法中使用时,this上下文就是组件。您甚至首先需要绑定的原因是,当您通过事件处理程序将函数作为一流对象传递时,this 上下文丢失了(因为您不再调用this.someMethod了)。
2021-05-23 01:28:24
@devserkan 好吧,这取决于。React 显式绑定this到其生命周期方法。至于其他类方法,这取决于您如何调用它们以及您是否自己提供上下文。如果你这样做,那么this将是组件,但如果不是(即当 React 在内部调用你的事件处理程序时),它不会被提供。
2021-05-27 01:28:24
惊人的。你解开了我的很多疑惑。非常感激。很好的解释哥们。感谢您花费宝贵的时间,您的回答现在对我来说真的很有value:)
2021-06-01 01:28:24
@Li357,你说得对。是我的错,措辞不好。范围就在那里,我们确实正在失去上下文。通过像你在这里所做的更正,我会更好地学习术语。我已经更新了我的答案,非常感谢。但是,我在这里有点困惑:“当你在生命周期方法或其他一些绑定方法中专门使用 this 时,这个上下文就是组件。” 如果该方法不是绑定的并且我在类中调用它,那么this上下文是否仍然是组件本身?
2021-06-02 01:28:24
我了解生命周期方法的情况。例如,对于其他方法,有一个方法只记录一个状态变量,我从componentDidMount. 绑定与否,这里不是类本身的上下文吗?你的“其他绑定方法”这句话让我很困惑,仅此而已。
2021-06-16 01:28:24

如果您有以下 React 代码,

class A extends React.Component {
  constructor(props) {
    super(props)
    this.state = {name: props.name}
  }
  render() {
    return (
       <button onclick={(event)=>console.log(this.state.name)} />
    )
  }
}

改为跟随

class A extends React.Component {
  state = {name: this.props.name}
  render() {
    return (
       <button onclick={this.logName} />
    )
  }
  logName = (event) => {
    console.log(this.state.name)
  }
}

这样,您就不会在每次渲染时都创建新的匿名函数。

每次运行代码时都会创建新的函数实例 ()=>,这不是magic看看下面的成员函数

class A {
  memberFuncWithBinding = () => {}
  memberFuncWithoutBinding(){}
}

当类被实例化时,这两个成员函数都只创建一次。同样,没有魔法,但首选上层成员函数,因为this在该函数内部使用时,您将拥有正确的 A 类 this 绑定。

编辑:看,在遇到问题之前不要尝试优化代码。每次渲染创建新函数都比较慢,但只有几分之一毫秒。

您是在寻求更多说明还是想将这个问题作为重复来结束?
2021-05-22 01:28:24
为了完整起见,以下是转换后箭头函数作为属性初始值设定项的样子:medium.com/@charpeni/...
2021-05-27 01:28:24
感谢 Eric Kim 和 widged 花费宝贵的时间来解释这个概念:)
2021-06-05 01:28:24
请注意,箭头函数(属性初始值设定项)选项仍处于试验阶段,而不是 ES6 的一部分。您将在 react 问题 - github.com/facebook/react/issues/9851 中找到有关此主题的进一步讨论我个人认为最好避免使用箭头函数作为属性初始值设定项。我喜欢使用箭头函数来保留尽可能多的我的代码,而不需要任何引用……而且我对特定上下文中的箭头函数引用 this 有问题。
2021-06-13 01:28:24
谢谢。请看一下这个线程接受的答案stackoverflow.com/questions/48699573/...
2021-06-14 01:28:24