Приложение 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 ответа

Решение

Недавно мне сказали, что мы не должны этого делать, что реквизиты - это то, что мы ожидаем от родителя, и если не соблюдается договор, позволяющий компоненту сломаться.

Точно, если реквизит в компоненте является необязательным, компонент (который отображает фактическое представление) должен обрабатывать это, а не родительский компонент.

Однако может возникнуть ситуация, когда родительский элемент должен прерваться, если какой-либо из контрактов дочерних компонентов нарушается. Я могу придумать два возможных способа справиться с этой ситуацией:

  1. Передача уведомления об ошибке дочерним компонентам, где, если что-то идет не так, дочерний компонент может сообщить об ошибке родительскому компоненту. Но это не чистое решение, потому что если есть N дочерних, и если более одного сломается (или сообщит об ошибке) родителю, у вас не будет никакой подсказки, и вам будет трудно управлять.[Это вообще не эффективно, но здесь написано, потому что Я привык следовать этому, когда я изучал React:P]

  2. С помощью 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
    ...
  );
}

Приведенный выше пример для строки, и нам нужна другая реализация для других типов данных.

Удачи.

Другие вопросы по тегам