Нажмите здесь, чтобы опубликовать эту статью в LinkedIn »

Большинство новичков в разработке React обычно задают этот вопрос: в чем разница и какой из них я должен использовать? В этой статье мы постараемся понять, в чем разница и какую из них вам следует использовать. Давайте начнем.

Прежде всего, нам нужно понять, что это за два компонента:

Функциональный (также известный как без состояния) компонент - это простая функция javascript, которая принимает реквизиты в качестве аргумента и возвращает элемент реакции.

const MyStatelessComponent = props => <div>{props.name}</div>;
// without JSX
const MyStatelessComponent = props => React.createElement('div', null, props.name);

Компонент без состояния не имеет состояния (очевидно, не так ли?), Это означает, что вы не можете достичь `this.state` внутри него. У него также нет жизненного цикла, поэтому вы не можете использовать componentDidMount и другие хуки.

Когда я сказал, что он должен возвращать элемент реакции, это могло быть неясно для вас. Элемент React - это объект, который имеет 2 свойства (на самом деле больше, но сейчас нас интересуют только 2 свойства): type (строка), props (объект). Элемент нашего компонента без состояния будет выглядеть так:

{
   type: 'div',
   props: {
     children: props.name,
   }
}

Когда response отображает наш компонент без состояния, все, что ему нужно сделать, это просто вызвать функцию MyStatelessComponent и передать туда реквизиты. Вот и все.

Компонентный класс немного сложнее. У него есть состояние, хуки жизненного цикла и это класс javascript, что означает, что React создает его экземпляры. React должен инициализировать класс компонента, чтобы вызывать хуки жизненного цикла, вызывать конструктор, инициализировать состояние и т. Д.

class MyComponentClass extends React.Component {
  render() {
    return <div>{this.props.name}</div>;
  }
}

Вот как выглядел бы наш компонент без состояния, если бы он был написан как класс компонента. React ожидает, что он вернет элемент реакции из своей функции рендеринга. Если мы сравним, что возвращает функция рендеринга и что возвращает функцию компонента без сохранения состояния, мы увидим, что эти два элемента идентичны.

Теперь мы знаем, в чем разница, и какую из них использовать?

Мы знаем, что для создания экземпляра класса требуется больше времени, чем для вызова функции, верно? Что, если мы визуализируем 10000 элементов компонентов без состояния и компонентов класса? Какой из них будет быстрее? Я был удивлен, потому что между этими двумя рендерами нет разницы во времени. Вообще-то есть, но незначительно и всегда разное.

Тесты.

Это не так быстро, как мы ожидали, но есть еще одно отличие - количество кода. Если для компонента без состояния код после перевода его в ES5 выглядит так:

var MyStatelessComponent = function MyStatelessComponent(props) {
  return React.createElement(
    "div",
    null,
    props.name
  );
}

Класс компонента будет выглядеть так:

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var MyComponentClass = function (_React$Component) {
  _inherits(MyComponentClass, _React$Component);
function MyComponentClass() {
    _classCallCheck(this, MyComponentClass);
return _possibleConstructorReturn(this, (MyComponentClass.__proto__ || Object.getPrototypeOf(MyComponentClass)).apply(this, arguments));
  }
_createClass(MyComponentClass, [{
    key: "render",
    value: function render() {
      return React.createElement(
        "div",
        null,
        this.props.name
      );
    }
  }]);
return MyComponentClass;
}(React.Component);

Разница довольно большая, не правда ли? Конечно, он будет минимизирован после запуска через любой конструктор (webpack, gulp, grunt), но даже в этом случае разница все равно велика: 1,2 КБ против 97 байт.

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

Когда следует использовать компонент без состояния:

Компонент без состояния (или тупой) - это просто представление состояния (реквизиты). Он может отображать только реквизиты, и он должен только это делать. Хорошим примером является компонент кнопки: допустим, у нас есть кнопка, которую нужно специально стилизовать, поэтому мы создаем компонент кнопки без состояния, который будет выглядеть следующим образом:

const Button = props => (
   <button className="our_button" onClick={props.onClick}>
      {props.label}
   </button>
);

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

Когда следует использовать компонент класса:

Компонент класса следует использовать всякий раз, когда вам нужно работать с состоянием, это может быть сокращение, реле или внутреннее состояние реакции. Всякий раз, когда вам нужно получить данные перед рендерингом компонента, вы должны вызвать функцию fetchData в componentDidMount, чтобы быть уверенным, что ваши данные будут извлечены как можно скорее. Вы не можете использовать какие-либо побочные эффекты внутри функции рендеринга, поэтому вы должны создавать побочные эффекты внутри хуков жизненного цикла. Таким образом, правило будет таким: если вашему компоненту нужны некоторые данные, которые нельзя передать в качестве опоры, используйте компонент класса для получения этих данных. Если вам нужно сохранить состояние пользовательского интерфейса в вашем компоненте (расширяемые блоки), это хорошее место для хранения этой информации в состоянии компонентов.

Заключение. Небольшое резюме: функциональный компонент не имеет состояния, методов жизненного цикла и его легко написать (простая функция), компонент класса имеет состояние, методы жизненного цикла, и React создает экземпляр компонента класса каждый раз, когда React его визуализирует. Если вам не нужно использовать состояние или жизненный цикл, я бы порекомендовал вам использовать функциональный компонент, но если есть вероятность, что вам понадобится одна из этих вещей (состояние, методы жизненного цикла), я бы посоветовал вам использовать компонент класса.

Если вам понравился этот пост, пожалуйста, хлопните его и поделитесь :)