Приложение ReactJS - отказоустойчивость VS быстро падает
Я нахожусь в процессе разработки для приложения React, и именно этот подход я использовал для своих компонентов: я проверяю реквизиты, которые ожидаю получить, используя проверку PropTypes, но я по-прежнему назначаю значения по умолчанию, чтобы избежать этого ломается, если что-то идет не так с полученными данными.
Недавно мне сказали, что мы не должны этого делать, что реквизиты - это то, что мы ожидаем от родителя, и если не соблюдается договор, позволяющий компоненту сломаться.
Какой подход правильный и каковы плюсы и минусы?
Некоторые из моих соображений как пища для размышлений..
Следуя моему первоначальному подходу, в тестах я явно тестирую значения по умолчанию, передавая тестируемому компоненту некоторые недействительные данные и ожидая, что верный моментальный снимок все еще будет распечатан. Тесты не терпят неудачу из-за некоторых неверных данных, но я распечатываю предупреждения проверки PropTypes (которые могут быть преобразованы в ошибки при желании - я думаю - или замолчать, издеваясь над ними в тесте).
Эти предупреждения, как в тестах, так и в реальном приложении, являются более краткими и понятными, чем просто сообщение об ошибке, говорящей "не могу прочитать" someProp "из неопределенного" или аналогичной (и позволяющей React рендеринга обрыва цикла). Проверки правильности propType прямо и ясно сообщают вам, что вы сделали неправильно (вы указали неправильный тип как prop, пропада полностью отсутствовала и т. Д.).
При использовании второго подхода тесты не выполняются, потому что приложение ломается. Я думаю, что это хороший подход, только если тестовое покрытие действительно хорошее (90/100%), в противном случае это риск - он может сработать и сломаться в крайних случаях, разрушающих репутацию продукта. Рефакторинг или изменения требований происходят довольно часто, и некоторые крайние случаи могут приводить к нежелательным данным, которые нарушают работу приложения и не регистрируются в автоматических или ручных тестах.
Это означает, что когда приложение работает, код может сломаться в родительском компоненте из-за некоторых неверных данных, и все приложение перестает работать, тогда как вместо этого в первом случае приложение устойчиво и просто отображает некоторые пустые поля контролируемым образом.
Мысли?
Следует упрощенный пример:
Реагировать компонент
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,
};
Тестовое задание
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 (вместо того, чтобы позволить их отсутствию сломать компонент)
3 ответа
Недавно мне сказали, что мы не должны этого делать, что реквизиты - это то, что мы ожидаем от родителя, и если не соблюдается договор, позволяющий компоненту сломаться.
Точно, если реквизит в компоненте является необязательным, компонент (который отображает фактическое представление) должен обрабатывать это, а не родительский компонент.
Однако может возникнуть ситуация, когда родительский элемент должен прерваться, если какой-либо из контрактов дочерних компонентов нарушается. Я могу придумать два возможных способа справиться с этой ситуацией:
Передача уведомления об ошибке дочерним компонентам, где, если что-то идет не так, дочерний компонент может сообщить об ошибке родительскому компоненту. Но это не чистое решение, потому что если есть N дочерних, и если более одного сломается (или сообщит об ошибке) родителю, у вас не будет никакой подсказки, и вам будет трудно управлять.[Это вообще не эффективно, но здесь написано, потому что Я привык следовать этому, когда я изучал React:P]
С помощью
try/catch
в родительском компоненте и не доверяйте никакому дочернему компоненту вслепую и показывайте сообщения об ошибках, когда что-то идет не так. Когда вы используетеtry/catch
во всех ваших компонентах вы можете безопасно выбросить ошибку из компонентов, если какой-либо контракт не выполнен.
Какой подход правильный и каковы плюсы и минусы?
ИМО, второй подход (try/catch
в компонентах и выбрасывании ошибки, когда требования не выполнены) действует и решит все вопросы. При написании тестов для компонента, когда реквизиты не пройдены, вы можете ожидать ошибку при загрузке компонента.
Если вы используете React > 16, вот способ обработки ошибок.
Неправильно назначать значения по умолчанию для .isRequred
реквизит через компонент defaultProps
, Согласно официальным документам:
DefaultProps будет использоваться для гарантии того, что this.props.name будет иметь значение, если оно не было указано родительским компонентом. Проверка типов propTypes происходит после разрешения defaultProps, поэтому проверка типов также применяется к defaultProps.
Если вы установите значение свойства по умолчанию в Component.defaultProps, вы никогда не получите предупреждение, если этот объект не предоставлен родительским компонентом.
По моему мнению, я не позволю одному или двум отсутствующим атрибутам сломать мое приложение. React выступает в качестве слоя представления в моем приложении, и я думаю, что это далеко, когда показывается "Ой! Что-то не так", когда я не могу найти ключ в одном объекте. Это похоже на сообщение от сильно сломанного сервера со статусом 500, но мы знаем, что это определенно не так.
Для меня я создаю некоторые правила для обработки связи между функцией рендеринга и defaultProps:
Допустим, у нас есть пользовательский объект, переданный из parent:
defaultProps: {
user: {
avatar: {
small: ''
}
}
}
в функции рендера
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
...
);
}
Приведенный выше пример для строки, и нам нужна другая реализация для других типов данных.
Удачи.