React - defaultProps vs ES6默认params在解构时(性能问题)

时间:2017-01-05 15:19:52

标签: performance reactjs ecmascript-6 destructuring

在我的无状态功能组件中设置默认值时,我刚刚遇到了关于React性能的问题。

此组件的defaultProps定义了row: false,但我不喜欢它,因为文件末尾的defaultProps ,这实际上让人难以察觉。因此,我们不知道默认属性。所以我将它直接移动到函数声明中,并使用参数的ES6默认值进行分配。

const FormField = ({
  row = false,
  ...others,
}) => {
  // logic...
};

但后来我们与同事争论这个是不是好主意。因为这样做可能看起来微不足道,但也可能对性能产生很大的影响,因为反应不知道的默认值。

我相信在这种情况下,它是微不足道的。因为它是布尔值而不是对象/数组,因此在对帐期间不会被视为不同的值。

但是,让我们现在看一个更高级的用例:

const FormField = ({
  input: { name, value, ...inputRest },
  label = capitalize(name),
  placeholder = label,
  row = false,
  meta: { touched, error, warning },
  ...others,
}) => {
  // logic...
};

在此,我基于placeholder的{​​{1}}的值,label本身基于input.name。使用ES6解构和参数的默认值使整个事情很容易编写/理解,它就像一个魅力。

但这是个好主意吗?如果没有,那么你将如何正确地做到这一点?

3 个答案:

答案 0 :(得分:23)

我在Discord #reactiflux频道与几个人交谈,实际上得到了我想要的答案。

基本上有三个使用React组件的用例,在其中一些组件中,解构参数会影响性能,因此了解hunder的内容非常重要。

无状态功能组件

const MyComponent = ({ name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() }) => {
  return (
    <div>{displayName}</div>
  );
};

这是一个无状态的功能组件。没有状态,它是有用的,因为它不是Class实例,而是一个简单的函数。

在这种情况下,没有生命周期,您不能拥有componentWillMountshouldComponentUpdateconstructor。而且由于没有对生命周期的管理,因此对性能没有任何影响。此代码完全有效。有些人可能更愿意处理函数体内的默认displayName值,但最终它并不重要,它不会影响性能。

无状态非功能组件

(不要这样做!)

class MyComponent extends React.Component {
    render() {
        const { name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() } = this.props;
        return (
            <div>{displayName}</div>
          );
    }
}

这是一个无状态的非功能组件。没有国家,但它不是&#34;功能性&#34;因为它是class。因为它是一个类,扩展React.Component,这意味着你将拥有一个生命周期。您可以在那里componentWillMountshouldComponentUpdateconstructor

而且,因为它有一个生命周期,所以编写这个组件的方式是。但为什么呢?

简单地说,React提供defaultProps属性来处理默认道具值。在处理非功能组件时,最好使用它,因为所有依赖this.props的方法都会调用它。

之前的代码段创建了名为namedisplayName的新局部变量,但默认值仅适用于此render方法!。如果您希望为每个方法应用默认值,例如React生命周期(shouldComponentUpdate等)中的默认值,那么必须使用defaultProps代替。

因此,之前的代码实际上是一个错误,可能导致对name的默认值的误解。

以下是如何编写它来获得相同的行为:

class MyComponent extends React.Component {
    render() {
        const { name, displayName = humanize(name), address } = this.props;
        return (
            <div>{displayName}</div>
          );
    }
}

MyComponent.defaultProps = {
    name: 'John Doe',
    address: helper.getDefaultAddress(),
};

这样更好。因为如果没有定义,名称将始终为John Doe。还处理了address默认值,但没有displayName ...为什么?

好吧,我还没有找到解决这个特殊用例的方法。因为displayName应该基于name属性,我们在定义defaultProps时无法访问(AFAIK)。我看到的唯一方法是直接在render方法中处理它。也许有更好的方法。

我们对address属性没有这个问题,因为它不是基于MyComponent属性,而是依赖于完全独立且不需要道具的东西。

有状态的非功能组件

它与&#34;无状态非功能组件&#34;完全相同。因为仍然存在生命周期,所以行为将是相同的。在组件中有一个额外的内部state这一事实不会改变任何事情。

我希望这有助于理解何时使用组件进行解构。我非常喜欢这种功能性的方式,它更清洁恕我直言(简化为+1)。

您可能更喜欢始终使用defaultProps,无论是使用功能组件还是非功能组件,它都有效。 (+1表示一致性)

请注意非功能性组件的生命周期,并且需要&#34;使用defaultProps。但最终选择总是你的;)

答案 1 :(得分:3)

defaultProps与默认函数参数之间的一个很大区别是,将对照propTypes检查前者。 eslint-plugin-reactrequire-default-props规则解释得很好。

defaultProps优于代码中的自定义默认逻辑的一个优势是defaultPropsPropTypes类型检查发生之前被React解析,因此类型检查也将应用于您的{{1} }。无状态功能组件也是如此:默认功能参数的行为与defaultProps不同,因此仍然首选使用defaultProps

答案 2 :(得分:1)

查看您拥有的高级用例,您将为组件添加不必要的属性。 labelplaceholder依赖于传入的其他属性,在我看来,不应该作为组件本身的参数列出。

如果我在我的应用程序中尝试使用<FormField />并且我需要查看特定组件具有哪些依赖关系,我会对为什么要创建基于关闭的参数感到困惑其他参数。我会将labelplaceholder移动到函数体内,因此它清除它们不是组件依赖,而只是副作用。

就这方面的表现而言,我不确定两种方式都会有显着差异。无状态组件实际上没有有状态组件所做的“后备实例”,这意味着没有内存对象跟踪组件。我相信它只是传递参数和返回视图的纯函数。

在同样的说明中..添加PropTypes将有助于类型检查。

const FormField = ({
  input: { name, value, ...inputRest },
  row = false,
  meta: { touched, error, warning },
  ...others,
}) => {
  const label = capitalize(name),
  const placeholder = label,

  return (
    // logic
  );
};

FormField.propTypes = {
  input: PropTypes.shape({
    name: PropTypes.string.isRequired,
    value: PropTypes.string,
  }).isRequired,
  meta: PropTypes.shape({
    touched: PropTypes.bool.isRequired,
    error: PropTypes.bool.isRequired,
    warning: PropTypes.bool.isRequired,
  }).isRequired,
  row: PropTypes.bool.isRequired,
};