Уловки и подсказки, чтобы вывести вашу проверку типа на новый уровень.

Для разработчиков React, которые еще не запрыгнули в поезд TypeScript, PropTypes - это образ жизни. Мы используем их ежедневно, чтобы добавить проверку типов во время выполнения к нашим компонентам React.

Но большинство из нас используют только встроенные PropTypes, такие как строка или число. Что еще мы можем сделать с PropTypes? Как они могут помочь нам создавать библиотеки и настраиваемые компоненты? Как мы можем использовать их, чтобы упразднить реквизиты и функциональность?

Давайте посмотрим на все странные и чудесные способы использования и злоупотребления PropTypes.

Начиная

Давайте посмотрим на следующий Button компонент.

import React from 'react';
const Button = props => {
  return <button {...props}>{props.children}</button>;
};
export default Button;

И посмотрите на это с помощью PropTypes.

import React from 'react';
import PropTypes from 'prop-types';
const Button = props => {
  return <button {...props}>{props.children}</button>;
};
Button.propTypes = {
  children: PropTypes.node,
};
export default Button;

Довольно захватывающий материал. Почти так же увлекательно, как и стандартный тестовый пример в create-react-app.

Давай займемся чем-нибудь поинтереснее

import React from 'react';
import PropTypes from 'prop-types';
const Button = props => {
  return (
    <button
      {...props}
      onClick={props.onClick}
      aria-disabled={props.disabled}
      disabled={props.disabled}
    >
      {props.children}
    </button>
  );
};
Button.propTypes = {
  children: PropTypes.node,
  disabled: PropTypes.bool,
  onClick: PropTypes.func.isRequired,
};
export default Button;

Я добавил несколько деталей к нашей кнопке. Я добавил обязательную опору onClick и необязательную опору disabled.

Подождите. Должен ли onClick требоваться при любых обстоятельствах? Что делать, если Button отключен? Если пользователь не может нажать кнопку, зачем нужен onClick?

Знаете ли вы, что PropTypes - это просто функции? Посмотрите, что мы можем сделать.

Button.propTypes = {
  children: PropTypes.node,
  disabled: PropTypes.bool,
  onClick(props, ...rest) {
    if (!props.disabled) {
      return PropTypes.func.isRequired(props, ...rest);
    }
    return PropTypes.func(props, ...rest);
  },
};

Прохладный! Теперь onClick требуется только тогда, когда Button не отключен.

Давайте пойдем еще дальше. У нас есть несколько вариантов использования ссылок, которые выглядят как кнопки. Давайте расширим наш Button, чтобы поддержать это.

Мы добавляем опору под названием as.

const Button = props => {
  const Element = props.as || 'button';
  return (
    <Element
      {...props}
      onClick={props.onClick}
      aria-disabled={props.disabled}
      disabled={props.disabled}
    >
      {props.children}
    </Element>
  );
};
Button.propTypes = {
  as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
  children: PropTypes.node,
  disabled: PropTypes.bool,
  onClick(props, ...rest) {
    if (!props.disabled) {
      return PropTypes.func.isRequired(props, ...rest);
    }
    return PropTypes.func(props, ...rest);
  },
};

Обратите внимание на PropType для as.

PropTypes.oneOfType([PropTypes.string, PropTypes.elementType])

Мы можем передать строку типа a или компонент типа Link.

Теперь мы можем отобразить ссылку Button вот так

<Button as="a" href="https://google.com">Google</Button>

Или даже настраиваемый элемент React. Даже react-router-link!

import { Link } from 'react-router-dom';
<Button as={Link} to="/foo/bar">Foo</Button>

Пользовательская обработка ошибок

Теперь, когда мы поддерживаем рендеринг нашего Button разными способами, мы можем столкнуться с некоторыми сложными ситуациями.

Как насчет этого?

<Button as="a" href="https://google.com" disabled>Google</Button>

Если вы догадались, что наш Button не отключен - поздравляем. Вы угадали. Так как же это исправить?

Давайте определим собственный onClick обработчик, который позаботится об этом случае.

const onClick = event => {
  if (props.disabled) {
    event.preventDefault();
    return false;
  }
  if (props.onClick) {
    return props.onClick(event);
  }
  return false;
};

Вызывая event.preventDefault, мы запрещаем браузеру переходить по ссылке.

Но теперь, когда мы передаем onClick функцию в отключенную Button, мы не получаем никаких предупреждений.

Давайте снова обновим наш onClick PropType.

onClick(props, propName, componentName, ...rest) {
  if (props.disabled) {
    throw new Error(
      `${componentName}: onClick has no effect when disabled is set`
    );
  }
  if (!props.disabled) {
    return PropTypes.func.isRequired(props, ...rest);
  }
  return PropTypes.func(props, ...rest);
}

Прохладный! Теперь мы получаем лучшую обратную связь, когда передаем недопустимые реквизиты.

Эти расширенные PropTypes упрощают обнаружение ошибок до того, как они попадут в рабочую среду. Их легко понять и легко использовать. До скорого!