React-分解时的defaultProps vs ES6默认参数(性能问题) [英] React - defaultProps vs ES6 default params when destructuring (performances issues)

查看:68
本文介绍了React-分解时的defaultProps vs ES6默认参数(性能问题)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是在一个无状态功能组件中设置默认值时遇到了一个关于React性能的问题.

此组件的defaultProps定义了row: false,但我不喜欢它,因为defaultProps位于文件的结尾 ,这实际上使查看起来更加困难.因此,我们不知道默认属性.因此,我将其直接移到函数声明中,并使用ES6的默认参数值对其进行了分配.

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

但是我们随后与同事讨论了是否是个好主意.因为这样做似乎很简单,但由于 react无法识别默认值,因此对性能也可能有很大影响.

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


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

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

在这里,我基于labelplaceholder值,该值本身是基于input.name的.将ES6解构与参数的默认值一起使用可使整件事很容易编写/理解,并且就像一个魅力.

但这是个好主意吗?如果没有,那么您将如何正确执行呢?

解决方案

我在Discord #reactiflux频道上与几个人进行了交谈,并得到了我一直在寻找的答案.

React组件基本上有三个用例,在其中一些中,破坏结构的参数会影响性能,因此了解幕后发生的事情很重要.

无状态功能组件

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

这是无状态的非功能组件.没有状态,但是由于它是class,所以不是功能性".并且由于它是扩展React.Component的类,因此意味着您将拥有一个生命周期.您可以在那里componentWillMountshouldComponentUpdateconstructor.

而且,由于它具有生命周期,因此编写此组件的方式很糟糕.但是为什么呢?

简而言之,React提供了一个defaultProps属性,用于处理默认的props值.实际上,在处理非功能组件时最好使用它,因为依赖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属性,而是依赖于完全独立的东西,不需要道具.

有状态的非功能组件

它的工作原理与无状态非功能组件"相同.因为仍然存在生命周期,所以行为将是相同的.组件中还有一个附加的内部state事实不会改变任何内容.


我希望这有助于了解在对组件使用解构时的情况.我真的很喜欢这种功能方式,它更简洁,恕我直言(为简单起见,+ 1).

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

只需注意非功能组件的生命周期,这些组件需要"使用defaultProps.但最后选择总是你自己;)


编辑10-2019 :defaultProps最终将在将来某个时刻从React API中删除,请参见 https://stackoverflow.com/a/56443098/2391795 https://RFC的github.com/reactjs/rfcs/pull/107 .

I just came across a question about React performances when settings default values in one of my stateless functional components.

This component had a defaultProps which defined row: false, but I didn't like it because the defaultProps is at the end of the file, which actually makes it harder to see. And thus, we aren't aware of the default property. So I moved it to the function declaration directly and assigned it using ES6 default value for parameters.

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

But then we argued with a coworker about this being a good idea or not. Because doing so may seem trivial, but may also have a great impact upon performances since react is not aware of the default value.

I believe in this case, it's trivial. Because it's a boolean and not an object/array and therefore won't be seen as a different value during reconciliation.


But, let's now see a more advanced use-case:

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

Here, I base the value of placeholder from label, which itself is based on input.name. Using ES6 destructuring with default values for parameters makes the whole thing quite easy to write/understand and it works like a charm.

But is it a good idea? And if not, then how would you do it properly?

解决方案

I talked to several people on Discord #reactiflux channel and actually got the answer I was looking for.

There are basically three use-case with React components, and in some of them, destructuring params will impact performances so it is important to understand what's going on hunder the hood.

Stateless functional component

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

This is a stateless, functional component. There is no state, and it is functional because it is not a Class instance, but a simple function.

In this case, there is no life-cycle, you cannot have a componentWillMount or shouldComponentUpdate or constructor there. And because there is no management of the life-cycle, there is no impact on performances whatsoever. This code is perfectly valid. Some may prefer to handle the default displayName value within the function body, but in the end it doesn't really matter, it won't impact performances.

Stateless non-functional component

(Do not do this!)

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

This is a stateless non-functional component. There is no state, but it is not "functional" since it is a class. And because it is a class, extending React.Component, it means you will have a life-cycle. You can have componentWillMount or shouldComponentUpdate or constructor there.

And, because it has a life-cycle, the way of writing this component is bad. But why?

Simply put, React offers a defaultProps attribute, to deal with default props values. And it is actually better to use it when dealing with non-functional components, because it will be called by all methods that rely on this.props.

The previous code snippet creates new local variables named name and displayName, but the default values are applied for this render method only!. If you want the default values to be applied for every method, such as the ones from the React life-cycle (shouldComponentUpdate, etc.) then you must use the defaultProps instead.

So, the previous code is actually a mistake that may lead to misunderstanding about the default value of name.

Here is how it should be written instead, to get the same behavior:

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(),
};

This is better. Because name will always be John Doe if it wasn't defined. address default value was also dealt with, but not displayName... Why?

Well, I haven't found a way around that special use-case yet. Because the displayName should be based on the name property, which we cannot access (AFAIK) when defining defaultProps. The only way I see is to deal with it in the render method directly. Maybe there is a better way.

We don't have this issue with the address property since it's not based on the MyComponent properties but rely on something totally independant which doesn't need the props.

Stateful non-functional component

It works exactly the same as "Stateless non-functional component". Because there is still a life-cycle the behavior will be the same. The fact that there is an additional internal state in the component won't change anything.


I hope this helps to understand when using destructuring with components. I really like the functional way, it's much cleaner IMHO (+1 for simplicity).

You may prefer to always use defaultProps, whether working with functional or non-functional components, it's also valid. (+1 for consistency)

Just be aware of the life-cycle with non-functional components which "requires" the use of defaultProps. But in the end the choice is always yours ;)


Edit 10-2019: defaultProps will eventually be removed from React API at some point in the future, see https://stackoverflow.com/a/56443098/2391795 and https://github.com/reactjs/rfcs/pull/107 for the RFC.

这篇关于React-分解时的defaultProps vs ES6默认参数(性能问题)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆