Жизненный цикл React-компонента состоит из четырех основных фаз:
✅ Монтирование (Mounting) — компонент создается и вставляется в DOM
✅ Обновление (Updating) — компонент перерисовывается при изменении props или state
✅ Размонтирование (Unmounting) — компонент удаляется из DOM
✅ Ошибки (Error Handling) — обработка ошибок в компонентах
| Фаза | Классовые компоненты | Функциональные компоненты |
|---|---|---|
| Монтирование | constructor, render, componentDidMount | useEffect(() => {}, []) |
| Обновление | componentDidUpdate | useEffect(() => {}, [deps]) |
| Размонтирование | componentWillUnmount | useEffect(() => { return () => {} }, []) |
| Ошибки | componentDidCatch, getDerivedStateFromError | Нет встроенной поддержки |
Ключевое правило: Используйте функциональные компоненты с хуками для новых проектов! 🎯
Представьте, что компонент — это актер в театре. У него есть время выхода на сцену (монтирование), время смены костюмов (обновление), время ухода со сцены (размонтирование) и время, когда он может споткнуться (обработка ошибок)! 🎭
Жизненный цикл — это серия этапов, через которые проходит компонент от создания до уничтожения:
// Простой пример жизненного цикла
function SimpleComponent() {
console.log('1. Рендер компонента');
useEffect(() => {
console.log('2. Компонент смонтирован');
return () => {
console.log('3. Компонент будет размонтирован');
};
}, []);
return <div>Простой компонент</div>;
}Монтирование — это когда компонент создается и вставляется в DOM.
class MountingExample extends Component {
constructor(props) {
super(props);
console.log('1. constructor');
this.state = { count: 0 };
}
static getDerivedStateFromProps(props, state) {
console.log('2. getDerivedStateFromProps');
return null; // Не изменяем состояние
}
componentDidMount() {
console.log('3. componentDidMount');
// Подписки, запросы к API
}
render() {
console.log('4. render');
return <div>Компонент смонтирован</div>;
}
}function MountingExample() {
console.log('1. Рендер компонента');
// useEffect с пустым массивом зависимостей = componentDidMount
useEffect(() => {
console.log('2. useEffect (аналог componentDidMount)');
// Очистка (аналог componentWillUnmount)
return () => {
console.log('3. Очистка useEffect');
};
}, []); // Пустой массив зависимостей
return <div>Компонент смонтирован</div>;
}Обновление происходит, когда компонент перерисовывается из-за изменений props или state.
class UpdatingExample extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
static getDerivedStateFromProps(props, state) {
console.log('1. getDerivedStateFromProps');
return null;
}
shouldComponentUpdate(nextProps, nextState) {
console.log('2. shouldComponentUpdate');
// Оптимизация: вернуть false, чтобы пропустить обновление
return true;
}
componentDidUpdate(prevProps, prevState) {
console.log('3. componentDidUpdate');
// Работа с DOM после обновления
}
render() {
console.log('4. render');
return (
<div>
<p>Счетчик: {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Увеличить
</button>
</div>
);
}
}function UpdatingExample() {
const [count, setCount] = useState(0);
console.log('1. Рендер компонента');
// useEffect с зависимостями = componentDidUpdate
useEffect(() => {
console.log('2. useEffect (аналог componentDidUpdate)');
// Эффекты при обновлении
// Очистка перед следующим эффектом
return () => {
console.log('3. Очистка предыдущего эффекта');
};
}, [count]); // Зависимость от count
return (
<div>
<p>Счетчик: {count}</p>
<button onClick={() => setCount(count + 1)}>
Увеличить
</button>
</div>
);
}Размонтирование — это когда компонент удаляется из DOM.
class UnmountingExample extends Component {
componentDidMount() {
// Создаем подписку
this.timer = setInterval(() => {
console.log('Таймер работает');
}, 1000);
}
componentWillUnmount() {
console.log('componentWillUnmount: Очистка ресурсов');
// Обязательная очистка!
clearInterval(this.timer);
}
render() {
return <div>Компонент скоро будет удален</div>;
}
}
// Использование с условным рендерингом
function App() {
const [showComponent, setShowComponent] = useState(true);
return (
<div>
<button onClick={() => setShowComponent(!showComponent)}>
{showComponent ? 'Скрыть' : 'Показать'} компонент
</button>
{showComponent && <UnmountingExample />}
</div>
);
}function UnmountingExample() {
useEffect(() => {
// Создаем подписку
const timer = setInterval(() => {
console.log('Таймер работает');
}, 1000);
// Функция очистки (аналог componentWillUnmount)
return () => {
console.log('Очистка: Остановка таймера');
clearInterval(timer);
};
}, []); // Пустой массив = только при монтировании
return <div>Компонент скоро будет удален</div>;
}
// Использование с условным рендерингом
function App() {
const [showComponent, setShowComponent] = useState(true);
return (
<div>
<button onClick={() => setShowComponent(!showComponent)}>
{showComponent ? 'Скрыть' : 'Показать'} компонент
</button>
{showComponent && <UnmountingExample />}
</div>
);
}Обработка ошибок позволяет перехватывать ошибки в дочерних компонентах.
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
// Перехват ошибок в дочерних компонентах
static getDerivedStateFromError(error) {
console.log('getDerivedStateFromError:', error);
// Обновляем состояние, чтобы показать запасной UI
return { hasError: true };
}
// Логирование ошибок
componentDidCatch(error, errorInfo) {
console.log('componentDidCatch:', error, errorInfo);
// Можно отправить ошибку в службу логирования
}
render() {
if (this.state.hasError) {
// Запасной UI
return <h1>Что-то пошло не так!</h1>;
}
return this.props.children;
}
}
// Использование
function App() {
return (
<ErrorBoundary>
<BuggyComponent />
</ErrorBoundary>
);
}
function BuggyComponent() {
throw new Error('Ошибка в компоненте!');
}// Для функциональных компонентов нет встроенной поддержки обработки ошибок
// Необходимо использовать классовый Error Boundary
function BuggyComponent() {
throw new Error('Ошибка в компоненте!');
}
// Оборачиваем в классовый Error Boundary
function App() {
return (
<ErrorBoundary>
<BuggyComponent />
</ErrorBoundary>
);
}// Классовый компонент
class UserProfileClass extends Component {
constructor(props) {
super(props);
this.state = { user: null, loading: true };
}
async componentDidMount() {
try {
const user = await fetchUser(this.props.userId);
this.setState({ user, loading: false });
} catch (error) {
this.setState({ loading: false });
}
}
render() {
const { user, loading } = this.state;
if (loading) return <div>Загрузка...</div>;
if (!user) return <div>Пользователь не найден</div>;
return <div>Привет, {user.name}!</div>;
}
}
// Функциональный компонент
function UserProfileFunction({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadUser = async () => {
try {
const userData = await fetchUser(userId);
setUser(userData);
} catch (error) {
// Обработка ошибки
} finally {
setLoading(false);
}
};
loadUser();
}, [userId]); // Зависимость от userId
if (loading) return <div>Загрузка...</div>;
if (!user) return <div>Пользователь не найден</div>;
return <div>Привет, {user.name}!</div>;
}// Классовый компонент
class WindowSizeClass 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>
Размер окна: {this.state.width} x {this.state.height}
</div>
);
}
}
// Функциональный компонент
function WindowSizeFunction() {
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>
Размер окна: {size.width} x {size.height}
</div>
);
}// ❌ Проблема: утечки памяти
function BadComponent() {
useEffect(() => {
const timer = setInterval(() => {
console.log('Таймер');
}, 1000);
// Забыли очистку!
}, []);
return <div>Компонент с утечкой памяти</div>;
}
// ✅ Решение: правильная очистка
function GoodComponent() {
useEffect(() => {
const timer = setInterval(() => {
console.log('Таймер');
}, 1000);
// Обязательная очистка
return () => {
clearInterval(timer);
};
}, []);
return <div>Компонент без утечек</div>;
}// ❌ Проблема: бесконечный цикл
function BadComponent({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [user]); // Неправильная зависимость!
return <div>{user?.name}</div>;
}
// ✅ Решение: правильные зависимости
function GoodComponent({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]); // Правильная зависимость
return <div>{user?.name}</div>;
}function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
// Связанные эффекты для одного источника данных
useEffect(() => {
const loadUserData = async () => {
const userData = await fetchUser(userId);
const userPosts = await fetchUserPosts(userId);
setUser(userData);
setPosts(userPosts);
};
loadUserData();
}, [userId]);
return (
<div>
<h1>{user?.name}</h1>
<div>{posts.length} постов</div>
</div>
);
}// Использование useMemo и useCallback
function OptimizedComponent({ userId, onUserSelect }) {
const [user, setUser] = useState(null);
// Мемоизация вычислений
const userDisplayName = useMemo(() => {
return user ? `${user.firstName} ${user.lastName}` : 'Гость';
}, [user]);
// Мемоизация колбэков
const handleSelect = useCallback(() => {
onUserSelect(userId);
}, [onUserSelect, userId]);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
return (
<div>
<h1>{userDisplayName}</h1>
<button onClick={handleSelect}>Выбрать пользователя</button>
</div>
);
}// Используйте только если:
// 1. Поддерживаете legacy код
// 2. Нужна обработка ошибок (Error Boundaries)
// 3. Работаете с React версией < 16.8
class LegacyComponent extends Component {
// ... реализация с методами жизненного цикла
}// ✅ Используйте всегда для новых проектов
function ModernComponent() {
// ... реализация с хуками
}Жизненный цикл компонента — это как сценарий пьесы, где у каждого актера (компонента) есть время выхода, смены костюмов, ухода со сцены и спасения при падении! 🎭
Классовые компоненты:
Функциональные компоненты:
Практические советы:
Жизненный цикл — основа понимания React! 💪
Хотите больше полезных статей о React? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и прокачивайтесь каждый день! 🚀