← Назад к вопросам

Сможешь ли переписать классовый компонент на функциональный

2.2 Middle🔥 202 комментариев
#JavaScript Core

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Ответ: Переписывание классового компонента на функциональный

Да, я могу и с удовольствием перепишу классовый компонент на функциональный. Это стандартная практика в современной React-разработке, так как функциональные компоненты с хуками стали предпочтительным способом написания компонентов после выхода React 16.8. Функциональные компоненты предлагают более простой и лаконичный синтаксис, улучшенную читаемость и лучшее разделение логики.

Ключевые отличия и правила преобразования

  1. Убрать класс и render-метод: Вся логика компонента теперь будет внутри функции.
  2. Заменить this.state на useState: Каждый кусочек состояния инициализируется своим хуком.
  3. Заменить методы жизненного цикла на useEffect: componentDidMount, componentDidUpdate и componentWillUnmount объединяются в один хук.
  4. Заменить this.props на прямой доступ к props: Аргументы функции — это и есть props.
  5. Контекст (this): Проблемы с привязкой this исчезают, так как его больше нет.
  6. Рефы: createRef() и this.refName заменяются на useRef().

Пример преобразования

Рассмотрим классический классовый компонент — счётчик с подпиской на внешнее событие.

Исходный классовый компонент:

import React from 'react';

class ClassCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      width: window.innerWidth
    };
    this.handleResize = this.handleResize.bind(this);
  }

  componentDidMount() {
    document.title = `Вы нажали ${this.state.count} раз`;
    window.addEventListener('resize', this.handleResize);
  }

  componentDidUpdate() {
    document.title = `Вы нажали ${this.state.count} раз`;
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize);
  }

  handleResize() {
    this.setState({ width: window.innerWidth });
  }

  increment = () => {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  };

  render() {
    return (
      <div>
        <p>Счёт: {this.state.count}</p>
        <p>Ширина окна: {this.state.width}px</p>
        <button onClick={this.increment}>Увеличить</button>
      </div>
    );
  }
}

Функциональный компонент после рефакторинга:

import React, { useState, useEffect, useCallback } from 'react';

const FunctionCounter = ({ initialCount = 0 }) => { // 1. Props как аргумент
  // 2. Состояние через useState
  const [count, setCount] = useState(initialCount);
  const [width, setWidth] = useState(window.innerWidth);

  // 3. Мемоизированный обработчик для стабильной ссылки
  const handleResize = useCallback(() => {
    setWidth(window.innerWidth);
  }, []);

  // 4. Замена всех методов жизненного цикла на useEffect
  // Эффект для обновления заголовка (componentDidMount + componentDidUpdate для count)
  useEffect(() => {
    document.title = `Вы нажали ${count} раз`;
  }, [count]); // Зависимость: эффект сработает при изменении count

  // Эффект для подписки на событие resize (только componentDidMount и componentWillUnmount)
  useEffect(() => {
    window.addEventListener('resize', handleResize);
    // Функция очистки (аналог componentWillUnmount)
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, [handleResize]); // Зависимость от мемоизированного обработчика

  // 5. Логика методов становится обычными функциями
  const increment = () => {
    setCount(prevCount => prevCount + 1); // Функциональное обновление состояния
  };

  // 6. Возврат JSX напрямую (без render)
  return (
    <div>
      <p>Счёт: {count}</p>
      <p>Ширина окна: {width}px</    p>
      <button onClick={increment}>Увеличить</button>
    </div>
  );
};

export default FunctionCounter;

Преимущества функционального подхода:

  • Лучшая читаемость и меньше шаблонного кода: Нет конструкторов, привязок методов или обращения через this.
  • Более эффективное разделение логики: Хуки позволяют выделять переиспользуемую логику в кастомные хуки (например, useWindowSize, useDocumentTitle), что невозможно с классовыми компонентами.
  • Избавление от сложных паттернов: Пропадает необходимость в рендер-пропсах и компонентах высшего порядка (HOC) для простых случаев, так как логику можно переиспользовать через хуки.
  • Оптимизация производительности: React.memo, useMemo и useCallback часто проще и интуитивнее, чем PureComponent или shouldComponentUpdate.
  • Единый способ работы с побочными эффектами: useEffect объединяет разрозненные методы жизненного цикла, что уменьшает количество багов, связанных с пропущенной логикой обновления или очистки.

Таким образом, переписывание не только возможно, но и является рекомендуемой практикой для модернизации кодовой базы. Оно приводит к более чистому, модульному и поддерживаемому коду. Ключевые концепции, которые нужно освоить для такого рефакторинга: useState, useEffect, useRef, useContext, а также правила зависимостей хуков.