> For the complete documentation index, see [llms.txt](https://purrweb.gitbook.io/react-course/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://purrweb.gitbook.io/react-course/funkcionalnye-vs-klassovye-komponenty.md).

# Функциональные vs классовые компоненты

В мире *React* разработки есть два способа написать компонент *React*. Один использует функцию, а другой использует класс. В последнее время все более популярными становятся функциональные компоненты, так почему же это?

Самый простой способ определить компонент в *React* - написать функцию *Javascript*.

```jsx
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
```

Это просто функция которая принимает *props* и возвращает элемент *React*. Но вы также можете использовать синтаксис ES6 для написания компонентов.

```jsx
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
```

Обе версии эквивалентны и дадут вам одинаковый результат. Вы можете спросить себя: "Когда мне использовать функцию, а когда классовый компонент?"

## 1. Различия между функциональными и классовыми компонентами.

Наиболее очевидное различие - это синтаксис. Функциональный компонент - это простая функция *JavaScript*, которая принимает *props* в качестве аргумента и возвращает элемент *React*.

Компонент класса требует, чтобы вы расширили класс *React.Component* и создали функцию рендеринга, которая возвращает элемент *React*. Для этого потребуется больше кода.

Если вы посмотрите на транслированный код *Babel*, вы также увидите некоторые важные отличия:

```jsx
function Welcome(props) {
    return _react2.default.createElement(
        'h1',
        null,
        'Hello, '
        props.name
    );
}
```

```jsx
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 Welcome = function (_React$Component) {
  _inherits(Welcome, _React$Component);
function Welcome() {
    _classCallCheck(this, Welcome);
return _possibleConstructorReturn(this, (Welcome.__proto__ || Object.getPrototypeOf(MyComponentClass)).apply(this, arguments));
  }
_createClass(Welcome, [{
    key: "render",
    value: function render() {
      return React.createElement(
        "div",
        null,
        this.props.name
      );
    }
  }]);
return Welcome;
}(React.Component);
```

Разница довольно большая, не правда ли? Конечно, он будет минимизирован после запуска через любой builder ([webpack](https://webpack.js.org/), [gulp](https://gulpjs.com/), [grunt](https://gruntjs.com/)), но даже тогда разница все равно велика: 1,2 КБ против 97 байт.

## 2. Передача *props*.

Передача props может сбивать с толку, но давайте посмотрим, как они написаны как в классовых, так и в функциональных компонентах. Допустим, мы передаем *props* имени «John», как показано ниже.

```jsx
<Component name="John" />
```

```jsx
const FunctionalComponent = ({ name }) => {
 return <h1>Hello, {name}</h1>;
};
```

Внутри функционального компонента мы передаем *props* в качестве аргумента функции. Обратите внимание, что здесь мы используем деструктуризацию. В качестве альтернативы мы можем написать компонент и без нее:

```jsx
const FunctionalComponent = (props) => {
 return <h1>Hello, {props.name}</h1>;
};
```

В этом случае вы должны использовать *props.name* вместо *name*.

```jsx
class ClassComponent extends React.Component {
  render() {
    const { name } = this.props;
    return <h1>Hello, { name }</h1>;
 }
}
```

Поскольку это класс, вам нужно использовать *this* для ссылки на *props*. И, конечно же, мы можем использовать деструктуризацию, чтобы получить имя внутри *props*, используя компоненты на основе классов.

## 3. Использование *state.*

Мы все знаем, что не можем избежать работы с переменными состояния в проекте *React*. До недавнего времени обработка состояния была возможна только в компоненте класса, но с React 16.8 был представлен хук *useState*, позволяющий разработчикам писать функциональные компоненты с использованием состояния. Вы можете узнать больше о хуках из [официальной документации](https://reactjs.org/docs/hooks-overview.html). Здесь мы собираемся создать простой счетчик, который начинается с 0, и на одно нажатие на кнопку увеличит счетчик на 1.

### Использование *state* в функциональных компонентах:

```jsx
const FunctionalComponent = () => {
 const [count, setCount] = React.useState(0);

 return (
   <div>
     <p>count: {count}</p>
     <button onClick={() => setCount(count + 1)}>Click</button>
   </div>
 );
};
```

Чтобы использовать *state* в функциональном компоненте, нам нужно использовать *useState* хук, который принимает initial state. Мы начинаем с 0 щелчков, поэтому *initial state* счетчика будет 0.

Конечно, у вас могут быть разные *initial state*, включая *null*, строку или даже объект - любой тип, который позволяет *JavaScript*. А с левой стороны, поскольку *useState* возвращает текущее состояние и функцию, которая его обновляет, мы деструктурируем массив следующим образом. Если вас немного смущают два элемента массива, вы можете рассматривать их как состояние и его установщик. В этом примере мы назвали их *count* и *setCount*, чтобы облегчить понимание связи между ними.

### Использование state в классовых компонентах:

```jsx
class ClassComponent extends React.Component {
 constructor(props) {
   super(props);
   this.state = {
     count: 0
   };
 }

 render() {
   return (
     <div>
       <p>count: {this.state.count} times</p>
       <button onClick={() => this.setState({ count: this.state.count + 1 })}>
         Click
       </button>
     </div>
   );
 }
}
```

Идея все та же, но компонент класса обрабатывает состояние немного иначе. Во-первых, нам нужно понять важность конструктора *React.Component*. Вот определение из официальной документации:

«Конструктор компонента *React* вызывается перед его монтированием. При реализации конструктора для подкласса *React.Component* вы должны вызывать *super(props)* перед любым другим оператором. В противном случае *this.props* будет неопределенным в конструкторе, что может привести к ошибкам ».

По сути, без реализации конструктора и вызова *super(props)* все переменные состояния, которые вы пытаетесь использовать, будут неопределенными. Итак, давайте сначала определим конструктор. Внутри конструктора вы создадите объект состояния с ключом состояния и начальным значением. А внутри JSX мы используем *this.state.count* для доступа к значению ключа состояния, который мы определили в конструкторе для отображения счетчика. Сеттер почти такой же, только другой синтаксис.

В качестве альтернативы вы можете написать функцию *onClick*. Помните, что функция *setState* принимает аргумент(ы) *state*, при необходимости *props*(необязательно).

```jsx
onClick={() =>
  this.setState((state) => {
    return { count: state.count + 1 };
  })
}
```

## 4. Методы жизненного цикла.

Наконец, поговорим о жизненных циклах. Как вы уже знаете, жизненные циклы играют важную роль во время рендеринга. Тем из вас, кто переходит от компонентов класса к функциональным компонентам, должно быть интересно, что может заменить такие методы жизненного цикла, как *componentDidMount(), componentWillUnmount()* в компоненте класса. И да, есть хук, который идеально подходит для этой цели:

### При монтировании (*componentDidMount*).

Метод жизненного цикла *componentDidMount* вызывается сразу после завершения первого рендеринга. Раньше был *componentWillMount*, который происходит до первого рендеринга, но он является устаревшим и не рекомендуется для использования в новых версиях *React*.

```jsx
const FunctionalComponent = () => {
 React.useEffect(() => {
   console.log("Hello");
 }, []);
 return <h1>Hello, World</h1>;
};
```

Заменив *componentDidMount*, мы используем хук *useEffect* со вторым аргументом \[]. Второй аргумент хука *useState* обычно представляет собой массив состояний, которые меняются, и *useEffect* будет вызываться только при этих выбранных изменениях. Но когда это пустой массив, как в этом примере, он будет вызываться один раз при монтировании. Это прекрасная замена *componentDidMount*.

```jsx
class ClassComponent extends React.Component {
 componentDidMount() {
   console.log("Hello");
 }

 render() {
   return <h1>Hello, World</h1>;
 }
}
```

По сути, здесь происходит то же самое: *componentDidMount* - это метод жизненного цикла, который вызывается один раз после первого рендеринга.

### При размонтировании (*componentWillUnmount*).

```jsx
const FunctionalComponent = () => {
 React.useEffect(() => {
   return () => {
     console.log("Bye");
   };
 }, []);
 return <h1>Bye, World</h1>;
};
```

Мы также можем использовать хук *useState* для *unmount* компонента. Но будьте осторожны, синтаксис немного другой. Что вам нужно сделать, так это вернуть функцию, которая запускается при размонтировании внутри функции *useEffect*. Это особенно полезно, когда вам нужно очистить подписки, такие как функция *clearInterval*, иначе это может вызвать серьезную утечку памяти в более крупном проекте. Одним из преимуществ использования *useEffect* является то, что мы можем писать функции для монтирования и размонтирования в одном и том же месте.

```jsx
class ClassComponent extends React.Component {
 componentWillUnmount() {
   console.log("Bye");
 }

 render() {
   return <h1>Bye, World</h1>;
 }
}
```

## 5. Заключение.

У обоих стилей есть свои плюсы и минусы, но я хотел бы сделать вывод, что в обозримом будущем функциональные компоненты вытеснят классовые в React.

Как мы заметили в примерах, функциональный компонент написан короче и проще, что упрощает разработку, понимание и тестирование. Использование функциональных компонентов позволяет легко избежать беспорядка и сохранить все в чистоте.

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

Опять же, в React есть много допустимых стилей кодинга. Тем не менее, я предпочитаю использовать функциональные компоненты вместо компонентов класса по причинам, перечисленным выше.
