Какая разница между функциональными и классовыми компонентами?

👨‍💻 Frontend Developer 🟡 Часто попадается 🎚️ Средний
#React

Краткий ответ

Функциональные и классовые компоненты — это два способа создания компонентов в React, но с ключевыми различиями:

ХарактеристикаФункциональныеКлассовые
СинтаксисПроще и корочеБолее многословный
СостояниеХуки (useState)this.state
Жизненный циклХуки (useEffect)Методы класса
ПроизводительностьЛучшеХуже
thisНе требуетсяТребуется

Современный подход:

  • Функциональные компоненты — предпочтительный способ
  • Классовые компоненты — устаревший, но все еще поддерживаются

Полный ответ

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

Что такое функциональные компоненты

Функциональные компоненты — это обычные JavaScript-функции, которые принимают props и возвращают JSX:

// Функциональный компонент
import { useState } from 'react';
 
function Welcome({ name }) {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <h1>Привет, {name}!</h1>
      <p>Счетчик: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Увеличить
      </button>
    </div>
  );
}

Что такое классовые компоненты

Классовые компоненты — это ES6-классы, которые наследуются от React.Component:

// Классовый компонент
import { Component } from 'react';
 
class Welcome extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  
  render() {
    return (
      <div>
        <h1>Привет, {this.props.name}!</h1>
        <p>Счетчик: {this.state.count}</p>
        <button onClick={() => this.setState({ 
          count: this.state.count + 1 
        })}>
          Увеличить
        </button>
      </div>
    );
  }
}

Основные различия

1. Синтаксис

Функциональные компоненты проще и короче:

// Функциональный компонент
function Greeting({ name }) {
  return <h1>Привет, {name}!</h1>;
}

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

// Классовый компонент
class Greeting extends Component {
  render() {
    return <h1>Привет, {this.props.name}!</h1>;
  }
}

2. Работа с состоянием

Функциональные компоненты используют хуки:

import { useState } from 'react';
 
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Счетчик: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Увеличить
      </button>
    </div>
  );
}

Классовые компоненты используют this.state:

import { Component } from 'react';
 
class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  
  render() {
    return (
      <div>
        <p>Счетчик: {this.state.count}</p>
        <button onClick={() => this.setState({ 
          count: this.state.count + 1 
        })}>
          Увеличить
        </button>
      </div>
    );
  }
}

3. Жизненный цикл

Функциональные компоненты используют хуки:

import { useEffect, useState } from 'react';
 
function DataFetcher({ userId }) {
  const [data, setData] = useState(null);
  
  // Заменяет componentDidMount, componentDidUpdate, componentWillUnmount
  useEffect(() => {
    fetchData(userId).then(setData);
    
    // Очистка (componentWillUnmount)
    return () => {
      // Отмена запросов, очистка таймеров
    };
  }, [userId]); // Зависимости
  
  return <div>{data ? JSON.stringify(data) : 'Загрузка...'}</div>;
}

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

import { Component } from 'react';
 
class DataFetcher extends Component {
  constructor(props) {
    super(props);
    this.state = { data: null };
  }
  
  // componentDidMount
  componentDidMount() {
    this.fetchData(this.props.userId);
  }
  
  // componentDidUpdate
  componentDidUpdate(prevProps) {
    if (prevProps.userId !== this.props.userId) {
      this.fetchData(this.props.userId);
    }
  }
  
  // componentWillUnmount
  componentWillUnmount() {
    // Отмена запросов, очистка таймеров
  }
  
  fetchData = async (userId) => {
    const data = await fetchData(userId);
    this.setState({ data });
  }
  
  render() {
    return (
      <div>
        {this.state.data ? JSON.stringify(this.state.data) : 'Загрузка...'}
      </div>
    );
  }
}

4. Производительность

Функциональные компоненты обычно быстрее:

// Функциональный компонент - меньше накладных расходов
function FastComponent({ data }) {
  return (
    <ul>
      {data.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

Классовые компоненты имеют больше накладных расходов:

// Классовый компонент - больше накладных расходов
class SlowerComponent extends Component {
  render() {
    return (
      <ul>
        {this.props.data.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    );
  }
}

Когда использовать каждый подход

Функциональные компоненты (предпочтительный подход)

Использовать всегда, когда возможно:

  • Современный стандарт React
  • Лучшая читаемость кода
  • Меньше boilerplate-кода
  • Лучшая производительность
  • Поддержка хуков
// Рекомендуемый подход
import { useState, useEffect } from 'react';
 
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    const fetchUser = async () => {
      setLoading(true);
      const userData = await fetch(`/api/users/${userId}`).then(r => r.json());
      setUser(userData);
      setLoading(false);
    };
    
    fetchUser();
  }, [userId]);
  
  if (loading) return <div>Загрузка...</div>;
  if (!user) return <div>Пользователь не найден</div>;
  
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

Классовые компоненты (устаревший подход)

Использовать только при необходимости:

  • Поддержка старого кода
  • Работа с legacy-проектами
  • Специфические случаи, где хуки не подходят
// Только для legacy-кода
import { Component } from 'react';
 
class LegacyUserProfile extends Component {
  constructor(props) {
    super(props);
    this.state = { user: null, loading: true };
  }
  
  componentDidMount() {
    this.fetchUser();
  }
  
  componentDidUpdate(prevProps) {
    if (prevProps.userId !== this.props.userId) {
      this.fetchUser();
    }
  }
  
  fetchUser = async () => {
    this.setState({ loading: true });
    const userData = await fetch(`/api/users/${this.props.userId}`).then(r => r.json());
    this.setState({ user: userData, loading: false });
  }
  
  render() {
    if (this.state.loading) return <div>Загрузка...</div>;
    if (!this.state.user) return <div>Пользователь не найден</div>;
    
    return (
      <div>
        <h1>{this.state.user.name}</h1>
        <p>{this.state.user.email}</p>
      </div>
    );
  }
}

Практические примеры

1. Простой компонент

Функциональный подход:

import { useState } from 'react';
 
function Toggle() {
  const [isOn, setIsOn] = useState(false);
  
  return (
    <button onClick={() => setIsOn(!isOn)}>
      {isOn ? 'ВКЛ' : 'ВЫКЛ'}
    </button>
  );
}

Классовый подход:

import { Component } from 'react';
 
class Toggle extends Component {
  constructor(props) {
    super(props);
    this.state = { isOn: false };
  }
  
  render() {
    return (
      <button onClick={() => this.setState({ 
        isOn: !this.state.isOn 
      })}>
        {this.state.isOn ? 'ВКЛ' : 'ВЫКЛ'}
      </button>
    );
  }
}

2. Компонент с побочными эффектами

Функциональный подход:

import { useEffect, useState } from 'react';
 
function WindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });
  
  useEffect(() => {
    const handleResize = () => {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight
      });
    };
    
    window.addEventListener('resize', handleResize);
    
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);
  
  return (
    <div>
      <p>Ширина: {size.width}px</p>
      <p>Высота: {size.height}px</p>
    </div>
  );
}

Классовый подход:

import { Component } from 'react';
 
class WindowSize extends Component {
  constructor(props) {
    super(props);
    this.state = {
      width: window.innerWidth,
      height: window.innerHeight
    };
  }
  
  componentDidMount() {
    window.addEventListener('resize', this.handleResize);
  }
  
  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize);
  }
  
  handleResize = () => {
    this.setState({
      width: window.innerWidth,
      height: window.innerHeight
    });
  }
  
  render() {
    return (
      <div>
        <p>Ширина: {this.state.width}px</p>
        <p>Высота: {this.state.height}px</p>
      </div>
    );
  }
}

Резюме

Функциональные и классовые компоненты — два способа создания компонентов в React:

Функциональные компоненты (современный подход):

  • Проще и короче
  • Используют хуки для состояния и эффектов
  • Лучше производительность
  • Предпочтительный способ в современном React

Классовые компоненты (устаревший подход):

  • Более многословные
  • Используют this.state и методы жизненного цикла
  • Больше накладных расходов
  • Используются только в legacy-коде

Ключевые моменты:

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

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


Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪