Чем отличается функциональный компонент от классового?
Функциональный компонент и классовый компонент в React — это два способа создания компонентов, которые выполняют одну и ту же задачу: описывают, как должен выглядеть и вести себя пользовательский интерфейс. Однако они различаются синтаксисом, подходом к управлению состоянием и жизненным циклом, а также исторически имели разное место в экосистеме React.
1. Синтаксис
Функциональные компоненты — это обычные JavaScript-функции, которые принимают props как аргумент и возвращают JSX.
function Welcome(props) {
return <h1>Привет, {props.name}</h1>;
}
или с использованием стрелочной функции:
const Welcome = ({ name }) => <h1>Привет, {name}</h1>;
Классовые компоненты — это ES6-классы, которые наследуются от React.Component. Они обязательно должны реализовать метод render, который возвращает JSX.
class Welcome extends React.Component {
render() {
return <h1>Привет, {this.props.name}</h1>;
}
}
2. Управление состоянием (state)
Функциональные компоненты (до 2019) не могли иметь своё состояние. Всё состояние приходилось поднимать вверх и передавать через props. Однако с появлением React Hooks (начиная с версии 16.8), функциональные компоненты получили возможность использовать состояние и другие функции жизненного цикла через хуки.
import { useState } from 'react';
function Counter() {
const \[count, setCount\] = useState(0);
return (
<>
<p>Счёт: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</>
);
}
Классовые компоненты изначально имели встроенное состояние через this.state и метод this.setState() для его изменения.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<>
<p>Счёт: {this.state.count}</p>
<button onClick={this.increment}>+1</button>
</>
);
}
}
3. Методы жизненного цикла (Lifecycle Methods)
Классовые компоненты используют специальные методы жизненного цикла, такие как:
-
componentDidMount
-
componentDidUpdate
-
componentWillUnmount
-
shouldComponentUpdate
-
getDerivedStateFromProps
-
и другие
class Timer extends React.Component {
componentDidMount() {
this.timer = setInterval(() => {
console.log('Тик');
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timer);
}
render() {
return <p>Таймер запущен</p>;
}
}
Функциональные компоненты реализуют жизненный цикл через хуки, такие как useEffect, который объединяет поведение componentDidMount, componentDidUpdate и componentWillUnmount.
import { useEffect } from 'react';
function Timer() {
useEffect(() => {
const timer = setInterval(() => {
console.log('Тик');
}, 1000);
return () => clearInterval(timer); // аналог componentWillUnmount
}, \[\]); // пустой массив — аналог componentDidMount
return <p>Таймер запущен</p>;
}
4. Чтение и обновление props
В функциональных компонентах props передаются как аргумент функции:
function Greeting(props) {
return <h1>{props.message}</h1>;
}
или с деструктуризацией:
const Greeting = ({ message }) => <h1>{message}</h1>;
В классовых компонентах props доступны через this.props:
class Greeting extends React.Component {
render() {
return <h1>{this.props.message}</h1>;
}
}
5. Контекст (Context API)
Обе модели могут использовать Context, но в функциональных компонентах можно применять хук useContext:
const value = useContext(MyContext);
В классовом компоненте:
class MyComponent extends React.Component {
static contextType = MyContext;
render() {
return <p>Значение: {this.context}</p>;
}
}
6. Читабельность и компактность
Функциональные компоненты:
-
Проще по синтаксису
-
Легче читаются
-
Меньше шаблонного кода
-
Лучше подходят для декларативного программирования
Классовые компоненты:
-
Более громоздкие
-
Требуют this, bind, конструкторов и методов жизненного цикла
-
Код может быть менее читаемым при усложнении логики
7. Поддержка хуков
Хуки (useState, useEffect, useMemo, useCallback, useReducer, и т.д.) можно использовать только в функциональных компонентах.
В классовых компонентах хуки недоступны. Логика должна реализовываться через методы жизненного цикла и классовое состояние.
8. Переиспользование логики
В функциональных компонентах повторно использовать логику можно с помощью пользовательских хуков:
function useCounter() {
const \[count, setCount\] = useState(0);
const increment = () => setCount(c => c + 1);
return { count, increment };
}
function Counter() {
const { count, increment } = useCounter();
return (
<>
<p>{count}</p>
<button onClick={increment}>+1</button>
</>
);
}
В классовых компонентах переиспользование логики чаще достигается через HOC (компоненты высшего порядка) или render props, что сложнее в реализации и понимании.
9. Обработка событий
В функциональных компонентах обработчики событий не требуют bind:
<button onClick={() => alert('Нажато')}>Нажми</button>
В классовом компоненте:
class MyButton extends React.Component {
handleClick = () => alert('Нажато');
render() {
return <button onClick={this.handleClick}>Нажми</button>;
}
}
Раньше нужно было использовать this.handleClick.bind(this) или объявлять обработчики через стрелочные функции в теле класса.
10. Поддержка и развитие
React-команда официально рекомендует использовать функциональные компоненты с хуками. Большинство новых библиотек, примеров и официальная документация ориентированы именно на этот подход.
Классовые компоненты продолжают поддерживаться, но не развиваются и не получают новых возможностей (например, новые хуки не могут быть использованы в классах).