在我的无状态功能组件中设置默认值时,我刚刚遇到了关于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解构和参数的默认值使整个事情很容易编写/理解,它就像一个魅力。
但这是个好主意吗?如果没有,那么你将如何正确地做到这一点?
答案 0 :(得分:23)
我在Discord #reactiflux频道与几个人交谈,实际上得到了我想要的答案。
基本上有三个使用React组件的用例,在其中一些组件中,解构参数会影响性能,因此了解hunder的内容非常重要。
const MyComponent = ({ name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() }) => {
return (
<div>{displayName}</div>
);
};
这是一个无状态的功能组件。没有状态,它是有用的,因为它不是Class
实例,而是一个简单的函数。
在这种情况下,没有生命周期,您不能拥有componentWillMount
或shouldComponentUpdate
或constructor
。而且由于没有对生命周期的管理,因此对性能没有任何影响。此代码完全有效。有些人可能更愿意处理函数体内的默认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
,这意味着你将拥有一个生命周期。您可以在那里componentWillMount
或shouldComponentUpdate
或constructor
。
而且,因为它有一个生命周期,所以编写这个组件的方式是坏。但为什么呢?
简单地说,React提供defaultProps
属性来处理默认道具值。在处理非功能组件时,最好使用它,因为所有依赖this.props
的方法都会调用它。
之前的代码段创建了名为name
和displayName
的新局部变量,但默认值仅适用于此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-react的require-default-props
规则解释得很好。
defaultProps
优于代码中的自定义默认逻辑的一个优势是defaultProps
在PropTypes
类型检查发生之前被React解析,因此类型检查也将应用于您的{{1} }。无状态功能组件也是如此:默认功能参数的行为与defaultProps
不同,因此仍然首选使用defaultProps
。
答案 2 :(得分:1)
查看您拥有的高级用例,您将为组件添加不必要的属性。 label
和placeholder
依赖于传入的其他属性,在我看来,不应该作为组件本身的参数列出。
如果我在我的应用程序中尝试使用<FormField />
并且我需要查看特定组件具有哪些依赖关系,我会对为什么要创建基于关闭的参数感到困惑其他参数。我会将label
和placeholder
移动到函数体内,因此它清除它们不是组件依赖,而只是副作用。
就这方面的表现而言,我不确定两种方式都会有显着差异。无状态组件实际上没有有状态组件所做的“后备实例”,这意味着没有内存对象跟踪组件。我相信它只是传递参数和返回视图的纯函数。
在同样的说明中..添加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,
};