# Функциональные 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 есть много допустимых стилей кодинга. Тем не менее, я предпочитаю использовать функциональные компоненты вместо компонентов класса по причинам, перечисленным выше.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://purrweb.gitbook.io/react-course/funkcionalnye-vs-klassovye-komponenty.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
