Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что делают основные хуки в React
Хуки (Hooks) это функции, которые позволяют использовать React функции (состояние, побочные эффекты и т.д.) в функциональных компонентах. Они были введены в React 16.8 и полностью изменили подход к разработке React приложений.
1. useState - управление состоянием
Самый важный хук. Позволяет функциональному компоненту иметь локальное состояние:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// count - текущее значение
// setCount - функция для обновления
// 0 - начальное значение
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
// Несколько состояний
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(null);
return (
<form>
<input value={name} onChange={e => setName(e.target.value)} />
<input value={email} onChange={e => setEmail(e.target.value)} />
</form>
);
}
// Со сложным состоянием
function TodoApp() {
const [todos, setTodos] = useState([]);
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text }]);
};
return <div>{/* ... */}</div>;
}
Важно: setCount запускает re-render компонента с новым значением.
2. useEffect - побочные эффекты
Позволяет выполнять код ПОСЛЕ рендера компонента. Это как componentDidMount, componentDidUpdate и componentWillUnmount вместе:
import { useEffect, useState } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
// Выполнить после КАЖДОГО рендера (опасно!)
useEffect(() => {
console.log('Компонент отрендерился');
});
// Выполнить только при МОНТИРОВАНИИ компонента
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []); // Пустой dependency array - выполнить один раз
// Выполнить, если user изменился
useEffect(() => {
console.log('user изменился:', user);
}, [user]); // Зависимость от user
// Очистка (cleanup) перед удалением компонента
useEffect(() => {
const timer = setInterval(() => {
console.log('Tick');
}, 1000);
// Функция для очистки ресурсов
return () => {
clearInterval(timer); // Очистить таймер
};
}, []);
if (loading) return <p>Loading...</p>;
return <div>{JSON.stringify(data)}</div>;
}
Правила:
- Зависимости в квадратных скобках контролируют, когда эффект выполняется
- [] - выполнить один раз при монтировании
- [dependency] - выполнить, когда dependency изменилась
- Нет массива - выполнить после каждого рендера (обычно плохо)
3. useContext - передача данных через компоненты
Избегает "prop drilling" - передачи props через множество промежуточных компонентов:
import { createContext, useContext } from 'react';
// Создать контекст
const ThemeContext = createContext();
// Провайдер (обертка вверху дерева компонентов)
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Header />
<Main />
<Footer />
</ThemeContext.Provider>
);
}
// Компонент, который использует контекст (в любом месте дерева)
function Button() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
style={{ background: theme === 'light' ? '#fff' : '#333' }}
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
>
Toggle theme
</button>
);
}
4. useReducer - сложное состояние
Для управления сложным состоянием с несколькими вариантами изменений:
import { useReducer } from 'react';
const initialState = { count: 0, error: null };
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1, error: null };
case 'DECREMENT':
return { count: state.count - 1, error: null };
case 'SET_ERROR':
return { ...state, error: action.payload };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>
Increment
</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>
Decrement
</button>
</div>
);
}
5. useCallback - мемоизация функций
Предотвращает пересоздание функции при каждом рендере:
import { useCallback, useMemo } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// БЕЗ useCallback - функция пересоздается каждый раз
const handleClick = () => {
console.log(count);
};
// С useCallback - функция не пересоздается, если count не изменился
const handleClickMemo = useCallback(() => {
console.log(count);
}, [count]); // Зависимость
return (
<div>
<Child onClick={handleClickMemo} />
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
// Child компонент
function Child({ onClick }) {
// Если onClick не изменилась - Child не re-renderes
console.log('Child rendered');
return <button onClick={onClick}>Click me</button>;
}
6. useMemo - мемоизация значений
Предотвращает дорогостоящие вычисления при каждом рендере:
import { useMemo } from 'react';
function ExpensiveComponent({ items, multiplier }) {
// БЕЗ useMemo - вычисляется при каждом рендере
const doubled = items.map(x => x * multiplier);
// С useMemo - вычисляется только если items или multiplier изменилась
const doubledMemo = useMemo(() => {
console.log('Computing...');
return items.map(x => x * multiplier);
}, [items, multiplier]);
return <div>{JSON.stringify(doubledMemo)}</div>;
}
7. useRef - прямой доступ к DOM
Создает "ящик" (mutable container), который сохраняет значение между рендерами:
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>Focus input</button>
</>
);
}
// Хранение мutable значения
function Timer() {
const intervalRef = useRef(null);
const [count, setCount] = useState(0);
const startTimer = () => {
intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
};
const stopTimer = () => {
clearInterval(intervalRef.current);
};
return (
<>
<p>{count}</p>
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Stop</button>
</>
);
}
8. useLayoutEffect - эффект ДО рендера
Выполняется ПЕРЕД браузером рисует экран (в отличие от useEffect, который - ПОСЛЕ):
import { useLayoutEffect, useState } from 'react';
function MeasureComponent() {
const [height, setHeight] = useState(0);
const ref = useRef(null);
// Выполнится ПЕРЕД тем, как браузер покажет компонент
useLayoutEffect(() => {
setHeight(ref.current.clientHeight);
}, []);
return <div ref={ref}>Height: {height}</div>;
}
Осторожно: useLayoutEffect может замедлить приложение, если вычисления тяжелые.
Правила использования хуков
- Вызывай только на верхнем уровне - не в циклах, условиях или вложенных функциях
- Вызывай только в компонентах или кастомных хуках
- Используй нужные зависимости в useEffect, useCallback, useMemo
// ❌ НЕПРАВИЛЬНО
function Component() {
if (condition) {
useState(); // Ошибка!
}
for (let i = 0; i < 10; i++) {
useEffect(() => {}); // Ошибка!
}
}
// ✅ ПРАВИЛЬНО
function Component() {
const [state, setState] = useState();
useEffect(() => {
if (condition) {
// Логика внутри эффекта
}
}, [condition]);
}
Кастомные хуки
Можно создавать собственные хуки для переиспользования логики:
// Кастомный хук для формы
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const handleChange = (e) => {
const { name, value } = e.target;
setValues(prev => ({ ...prev, [name]: value }));
};
const reset = () => setValues(initialValues);
return { values, handleChange, reset };
}
// Использование
function LoginForm() {
const { values, handleChange, reset } = useForm({ email: '', password: '' });
return (
<form onSubmit={() => { /* submit */ reset(); }}>
<input name="email" value={values.email} onChange={handleChange} />
<input name="password" value={values.password} onChange={handleChange} />
</form>
);
}