ReactJS应用程序 – 弹性VS快速失败

我正在开发一个React应用程序,这是我用于组件的方法:我使用PropTypesvalidation来validation我期望得到的道具,但我仍然指定默认值以避免它如果接收到的数据出错,就会中断。

最近有人告诉我,我们不应该这样做,道具是我们期望从父母那里得到的,如果合同不被允许让组件中断。

哪种方法是正确的,有什么优点和缺点?

我的一些考虑作为思考的食物。

按照我的初始方法,在testing中,我明确地testing了传递给被测组件的默认值一些无效的数据,并期望有效的快照仍然被打印出来。 testing不会因为一些不好的数据而失败,但是我打印出PropTypesvalidation警告(如果需要的话,虽然可以通过错误进行转换 – 我认为 – 或者在testing中嘲笑它们)。

这些警告在testing和实际应用程序中都比只看到“无法读取”someProp“未定义”或类似(并且让React渲染循环中断)的错误更加简洁明了。 propTypevalidation直接明确地告诉你你做错了什么(你通过错误的types作为道具,道具完全失踪等)。

使用第二种方法,而testing失败,因为应用程序中断。 我认为,只有当testing覆盖率真的很好(90/100%)时,这才是一个好方法,否则就是一种风险 – 它可能会在活动中破裂并破坏产品声誉。 重构或需求变更经常发生,一些边缘案例可能会以破坏应用程序的不希望的数据结束,而不是在自动或手动testing中捕获。

这意味着当应用程序处于活动状态时,由于某些不良数据,整个应用程序将停止工作,代码可能会在父组件中断,而在第一种情况下,应用程序具有弹性,并且以受控方式显示一些空的字段。

思考?

遵循一个简单的例子:

React组件

import React from 'react'; import PropTypes from 'prop-types'; import styles from './styles.css'; export const App = ({ person : { name, surname, address, subscription } = {} }) => ( <div style={styles.person}> <p> {person.name} </p> <p> {person.surname} </p> <p> {person.address} </p> <div> { person.subscription && <Subscription details={person.subscription} /> } </div> </div> ); // PS. this is incorrect in this example (as pointed out in an answer). Real code used inline initialization. // App.defaultProps = { // person: { subscription: undefined }, // }; App.propTypes = { person: PropTypes.shape({ name: PropTypes.string.isRequired, surname: PropTypes.string.isRequired, address: PropTypes.string, subscription: PropTypes.object, }).isRequired, }; 

testing

 import React from 'react'; import { shallow } from 'enzyme'; import { mockOut } from 'testUtils/mockOut'; import { App } from '../index.js'; describe('<App>', () => { mockout(App, 'Subscription'); it('renders correctly', () => { const testData = { name: 'a name', surname: 'a surname', address: '1232 Boulevard Street, NY', subscription: { some: 'data' }, } const tree = shallow(<App person={testData} />); expect(tree.html()).toMatchSnapshot(); }); it('is resilient in case of bad data - still generates PropTypes validation logs', () => { const tree = shallow(<App person={undefined} />); expect(tree.html()).toMatchSnapshot(); }); }); 

更新:

问题的焦点在于是否将默认值分配给用isRequired标记的道具是正确的(而不是让他们的缺席中断组件)

最近有人告诉我,我们不应该这样做,道具是我们期望从父母那里得到的,如果合同不被允许让组件中断。

确切地说,如果组件中的道具是可选的,组件(渲染实际的视图)应该处理,而不是父组件。

但是,如果任何子组件合同中断,您可能会遇到父项应该中断的情况。 我可以想到两种可能的方式来处理这种情况 –

  1. 将错误通知器传递给子组件,如果发生任何错误,子组件可以向父组件报告错误。 但这不是干净的解决办法,因为如果有N个孩子,如果有多个孩子会打破(或报告错误)给父母,你就不会有任何线索,而且很难pipe理。当我学习React时,我曾经遵循这一点:P]

  2. 在父组件中使用try/catch ,不要盲目信任任何子组件,并在出现错误时显示错误消息。 当您在所有组件中使用try/catch时,您可以安全地在任何合同未满足时从组件中抛出错误。

哪种方法是正确的,有什么优点和缺点?

国际海事组织,第二种方法( try/catch组件和抛出错误,当要求没有履行)是有效的,将解决所有问题。 在不通过道具的情况下编写组件的testing时,加载组件时可能会出现错误。

更新

如果你正在使用React> 16, 这里是处理错误的方法。

通过组件defaultProps将默认值分配给.isRequred道具是不正确的。 根据官方文件 :

如果未由父组件指定,则将使用defaultProps来确保this.props.name具有值。 propTypestypes检查发生在defaultProps被parsing之后,所以types检查也会应用到defaultProps。

如果您在Component.defaultProps中设置了默认的属性值,那么如果这个道具不是由父组件提供的,则永远不会得到警告。

在我看来,我不会让一两个缺失的属性来打破我的申请。 React在我的应用程序中充当表示层,我认为当我无法在一个对象中find关键字时,显示“糟糕!有什么问题”是远远不够的。 这看起来像是一个来自500个状态严重破碎的服务器的消息,但我们知道这绝对没有错。

对我来说,我确实build立了一些规则来处理渲染函数和defaultProps之间的通信:

比方说,我们有一个用父对象传递的用户对象:

 defaultProps: { user: { avatar: { small: '' } } } 

在渲染function

 render() { const { user } = this.props; // if user.avatar is not defined or user.avatar.small is empty string or undefined then we render another component we have prepared for this situation. if (!user.avatar || !user.avatar.small) { return ( // components ... ); } // normal situation return ( // components ... ); } 

上面的例子是针对string的,我们需要针对其他数据types的不同实现。

祝你好运。