Хуки — это специальные функции React, с помощью которых можно управлять состоянием, побочными эффектами и другими возможностями React внутри функциональных компонентов. Их основная цель — сделать функциональные компоненты такими же мощными, как и классовые, избегая сложных вложенных структур и дублирования кода.
Что делает: Позволяет объявлять внутреннее состояние в функциональных компонентах.
Как работает:
- При первом рендере значение из аргумента (например, 0) становится начальным для состояния.
- Возвращает массив:
[значение, функция для обновления]. - Можно создавать столько useState, сколько нужно разных переменных состояния.
Особенности:
- Обновление состояния асинхронное.
- При каждом обновлении состояния компонент перерисовывается.
Пример:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // начальное значение = 0
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}Что делает: Управляет побочными эффектами — запросы к API, подписки, работа с DOM.
Как работает:
- useEffect(callback, [dependencies])
callbackсрабатывает после DOM-отрисовки (рендера).- Если передать зависимость (массив второго аргумента), хук будет срабатывать только при изменении этих переменных.
- Если массив пуст, эффект выполнится только 1 раз после маунта (аналог componentDidMount).
Особенности:
- Можно возвращать функцию очистки (cleanup) — полезно для удаления таймеров, подписок перед размонтированием компонента.
Пример:
import { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => setSeconds(s => s + 1), 1000);
return () => clearInterval(interval); // очистка интервала при размонтировании
}, []);
return <div>Прошло секунд: {seconds}</div>;
}Что делает: Дает доступ к глобальному контексту без пропсов.
Как работает:
- Контекст создаётся через
createContext(defaultValue). - В любом компоненте можно получить актуальное значение через useContext(Context).
- Используется для передачи тем, локализации, данных пользователя и др.
Особенности:
- Изменение значения в Provider пересчитает все подписанные на контекст компоненты.
Пример:
import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function ThemeButton() {
const theme = useContext(ThemeContext);
return <button className={theme}>Тема: {theme}</button>;
}Что делает: Сохраняет изменяемое значение между рендерами без триггера перерисовки.
Как работает:
- Возвращает объект вида
{ current: значение }. - Обычно используется для сохранения ссылок на DOM-элементы, хранения таймеров, внешних значений.
Особенности:
- Изменение
.currentне вызывает повторный рендер. - Отлично подходит для инкапсуляции "промежуточных" данных.
Пример:
import { useRef } from 'react';
function InputFocus() {
const inputRef = useRef();
return (
<>
<input ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Фокус!</button>
</>
);
}Что делает: Мемоизирует (кеширует) результат функции, чтобы не выполнять дорогие вычисления при каждом рендере.
Как работает:
- Выполняет функцию только если изменяются зависимости из массива.
- Возвращает закешированный результат.
Особенности:
- Использовать для реально тяжелых вычислений, иначе усложняет код без ощутимой пользы.
- Не мемоизирует функции — для этого есть useCallback.
Пример:
import { useMemo, useState } from 'react';
function ExpensiveComponent({ num }) {
const [count, setCount] = useState(0);
const fib = useMemo(() => {
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
return fibonacci(num);
}, [num]);
return (
<div>
Fibonacci: {fib}
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}Что делает: Мемоизирует функцию, чтобы не создавать новую при каждом рендере.
Как работает:
- Возвращает ту же функцию между рендерами, пока не изменены зависимости.
- Полезно при передаче колбеков вниз по дереву и оптимизациях.
Особенности:
- Если колбек зависит от пропсов или стейта, их обязательно указать в массиве зависимостей.
Пример:
import { useState, useCallback } from 'react';
function MyComponent() {
const [value, setValue] = useState(0);
const increment = useCallback(() => {
setValue(v => v + 1);
}, []);
return <button onClick={increment}>{value}</button>;
}Что делает: Позволяет управлять сложным состоянием с помощью редьюсера (аналог Redux, но локально).
Как работает:
- Принимает функцию reducer(state, action) и начальное состояние.
- Возвращает массив: [state, dispatch].
- dispatch отправляют action, и reducer на его основе возвращает новое состояние.
Особенности:
- Отлично работает для сложного состояния, состоящего из нескольких полей или требующего сложной логики изменений.
Пример:
import { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<button onClick={() => dispatch({ type: 'increment' })}>
Счет: {state.count}
</button>
);
}- useState: для управления простыми данными, зависящими от пользователя.
- useEffect: для работы с асинхронностью, подписками, чтением локального хранилища и побочными задачами.
- useContext: когда нужно глобально прокидывать данные глубоко в дерево компонентов.
- useRef: когда нужен доступ к DOM-элементу или "устойчивое" значение между рендерами (например, для хранения id таймера).
- useMemo/useCallback: только при явных проблемах с производительностью, связанных с пересозданием функций и повторным рендером тяжелых вычислений или потомков.
- useReducer: если компонент управляет сложной логикой состояния или несколькими связанными данными.
- Хуки вызываются всегда на верхнем уровне функционального компонента.
- Хуки нельзя использовать внутри условий, циклов, вложенных функций (иначе React не может обеспечить корректность работы внутреннего стека хуков).
- Хуки используются только в функциях-компонентах или кастомных хуках, не в обычных JS-функциях и не в классах.
Создавайте собственные хуки для переиспользования логики между компонентами.
Пример:
import { useState, useEffect } from 'react';
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}Использование:
function Example() {
const width = useWindowWidth();
return <div>Ширина окна: {width}</div>;
}