← Назад к вопросам

Что делают основные хуки?

1.0 Junior🔥 231 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что делают основные хуки в 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 может замедлить приложение, если вычисления тяжелые.

Правила использования хуков

  1. Вызывай только на верхнем уровне - не в циклах, условиях или вложенных функциях
  2. Вызывай только в компонентах или кастомных хуках
  3. Используй нужные зависимости в 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>
  );
}
Что делают основные хуки? | PrepBro