React предоставляет несколько встроенных хуков для работы с состоянием, эффектами и другими возможностями функциональных компонентов:
Основные хуки:
useState — для управления локальным состояниемuseEffect — для работы с побочными эффектамиuseContext — для использования контекста ReactДополнительные хуки:
useReducer — альтернатива useState для сложного состоянияuseCallback — для мемоизации функцийuseMemo — для мемоизации значенийuseRef — для доступа к DOM-элементам и хранения мутирующих значенийuseLayoutEffect — синхронный аналог useEffectuseTransition — для управления приоритетами обновленийuseId — для генерации уникальных IDСоздание пользовательских хуков — позволяет выносить повторяющуюся логику в отдельные функции.
React Hooks — это функции, которые позволяют использовать состояние и другие возможности React без написания классов. Они появились в React 16.8 и стали революцией в разработке функциональных компонентов. 🚀
Самый часто используемый хук для управления локальным состоянием компонента:
// Простой пример счётчика
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Счётчик: {count}</p>
<button onClick={() => setCount(count + 1)}>
Увеличить
</button>
</div>
);
}Используется для выполнения побочных эффектов, таких как запросы к API, подписки, таймеры:
// Пример с подпиской и отпиской
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]);
return <div>{/* отображение сообщений */}</div>;
}Позволяет получать значения из контекста React без проп drilling:
// Пример использования темы
function Button() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background }}>
Кнопка
</button>
);
}Альтернатива useState для сложной логики обновления состояния:
// Пример счётчика с редьюсером
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Счётчик: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>
Увеличить
</button>
</div>
);
}Предотвращает ненужное создание функций при каждом рендере:
// Пример оптимизации колбэков
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Кнопка нажата');
}, []);
return <Child onClick={handleClick} />;
}Кэширует результаты вычислений между рендерами:
// Пример тяжёлых вычислений
function ExpensiveComponent({ items }) {
const expensiveValue = useMemo(() => {
return items.reduce((acc, item) => acc + item.value, 0);
}, [items]);
return <div>Результат: {expensiveValue}</div>;
}Позволяет получить доступ к DOM-элементам или хранить мутирующие значения:
// Пример фокусировки на элементе
function Form() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} />
<button onClick={focusInput}>Фокус</button>
</div>
);
}Аналог useEffect, но запускается синхронно после изменений DOM:
// Пример измерения элемента
function Tooltip() {
const [tooltipHeight, setTooltipHeight] = useState(0);
const tooltipRef = useRef();
useLayoutEffect(() => {
setTooltipHeight(tooltipRef.current.offsetHeight);
});
return (
<div ref={tooltipRef}>
Подсказка высотой {tooltipHeight}px
</div>
);
}Позволяет помечать обновления состояния как несрочные, чтобы не блокировать интерфейс:
// Пример с поиском с задержкой
function SearchComponent() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value);
// Помечаем обновление результатов как несрочное
startTransition(() => {
// Тяжёлая операция поиска
const filteredResults = heavySearchOperation(value);
setResults(filteredResults);
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <div>Загрузка...</div>}
<ResultsList results={results} />
</div>
);
}Генерирует стабильные уникальные ID, полезные для атрибутов доступности:
// Пример с созданием доступных форм
function FormField({ label, value, onChange }) {
const id = useId();
return (
<div>
<label htmlFor={id}>{label}</label>
<input
id={id}
value={value}
onChange={onChange}
aria-describedby={`${id}-description`}
/>
<p id={`${id}-description`}>
Это поле обязательно для заполнения
</p>
</div>
);
}Позволяют выносить повторяющуюся логику в переиспользуемые функции:
// Пример пользовательского хука для получения данных
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return { data, loading };
}
// Использование пользовательского хука
function UserProfile({ userId }) {
const { data, loading } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Загрузка...</div>;
return <div>Привет, {data.name}!</div>;
}// ❌ Не нужно мемоизировать простые значения
const value = useMemo(() => 5, []); // Ненужно!
// ✅ Просто используйте значение напрямую
const value = 5;// ❌ Пропущена зависимость
useEffect(() => {
console.log(userId); // userId используется, но не в зависимостях
}, []);
// ✅ Все зависимости указаны
useEffect(() => {
console.log(userId);
}, [userId]);// ❌ Мутирование массива
const [items, setItems] = useState([1, 2, 3]);
items.push(4); // Не будет работать!
// ✅ Создание нового массива
const [items, setItems] = useState([1, 2, 3]);
setItems([...items, 4]); // Правильно!// Хорошо: используем правильные хуки
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <div>Прошло: {seconds} секунд</div>;
}// Пользовательский хук для работы с локальным хранилищем
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = (value) => {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
};
return [storedValue, setValue];
}// Хорошо: зависимости соответствуют использованию
useEffect(() => {
if (userId) {
fetchUser(userId).then(setUser);
}
}, [userId]); // userId используется, значит должен быть в зависимостяхReact Hooks — это мощный инструмент для работы с состоянием и другими возможностями в функциональных компонентах:
✅ Основные хуки:
useState — для простого состоянияuseEffect — для побочных эффектовuseContext — для работы с контекстом✅ Дополнительные хуки:
useReducer — для сложного состоянияuseCallback/useMemo — для оптимизацииuseRef — для доступа к DOM и хранения значенийuseTransition — для управления приоритетами обновленийuseId — для генерации уникальных ID✅ Пользовательские хуки:
Понимание всех хуков и умение применять их правильно — ключ к эффективной разработке на React! 🎯
Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪