Какие знаешь Hooks в React?

👨‍💻 Frontend Developer 🟢 Почти точно будет 🎚️ Средний
#React #Hooks

Краткий ответ

React предоставляет несколько встроенных хуков для работы с состоянием, эффектами и другими возможностями функциональных компонентов:

Основные хуки:

  • useState — для управления локальным состоянием
  • useEffect — для работы с побочными эффектами
  • useContext — для использования контекста React

Дополнительные хуки:

  • useReducer — альтернатива useState для сложного состояния
  • useCallback — для мемоизации функций
  • useMemo — для мемоизации значений
  • useRef — для доступа к DOM-элементам и хранения мутирующих значений
  • useLayoutEffect — синхронный аналог useEffect
  • useTransition — для управления приоритетами обновлений
  • useId — для генерации уникальных ID

Создание пользовательских хуков — позволяет выносить повторяющуюся логику в отдельные функции.


Полный ответ

React Hooks — это функции, которые позволяют использовать состояние и другие возможности React без написания классов. Они появились в React 16.8 и стали революцией в разработке функциональных компонентов. 🚀

Основные встроенные хуки

1. useState — Управление состоянием

Самый часто используемый хук для управления локальным состоянием компонента:

// Простой пример счётчика
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Счётчик: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Увеличить
      </button>
    </div>
  );
}

2. useEffect — Побочные эффекты

Используется для выполнения побочных эффектов, таких как запросы к API, подписки, таймеры:

// Пример с подпиской и отпиской
function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);
  
  useEffect(() => {
    const connection = createConnection(roomId);
    connection.connect();
    
    return () => {
      connection.disconnect();
    };
  }, [roomId]);
  
  return <div>{/* отображение сообщений */}</div>;
}

3. useContext — Работа с контекстом

Позволяет получать значения из контекста React без проп drilling:

// Пример использования темы
function Button() {
  const theme = useContext(ThemeContext);
  
  return (
    <button style={{ background: theme.background }}>
      Кнопка
    </button>
  );
}

Дополнительные встроенные хуки

1. useReducer — Сложное состояние

Альтернатива 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>
  );
}

2. useCallback — Мемоизация функций

Предотвращает ненужное создание функций при каждом рендере:

// Пример оптимизации колбэков
function Parent() {
  const [count, setCount] = useState(0);
  
  const handleClick = useCallback(() => {
    console.log('Кнопка нажата');
  }, []);
  
  return <Child onClick={handleClick} />;
}

3. useMemo — Мемоизация значений

Кэширует результаты вычислений между рендерами:

// Пример тяжёлых вычислений
function ExpensiveComponent({ items }) {
  const expensiveValue = useMemo(() => {
    return items.reduce((acc, item) => acc + item.value, 0);
  }, [items]);
  
  return <div>Результат: {expensiveValue}</div>;
}

4. useRef — Доступ к DOM и мутирующие значения

Позволяет получить доступ к DOM-элементам или хранить мутирующие значения:

// Пример фокусировки на элементе
function Form() {
  const inputRef = useRef(null);
  
  const focusInput = () => {
    inputRef.current.focus();
  };
  
  return (
    <div>
      <input ref={inputRef} />
      <button onClick={focusInput}>Фокус</button>
    </div>
  );
}

5. useLayoutEffect — Синхронные эффекты

Аналог useEffect, но запускается синхронно после изменений DOM:

// Пример измерения элемента
function Tooltip() {
  const [tooltipHeight, setTooltipHeight] = useState(0);
  const tooltipRef = useRef();
  
  useLayoutEffect(() => {
    setTooltipHeight(tooltipRef.current.offsetHeight);
  });
  
  return (
    <div ref={tooltipRef}>
      Подсказка высотой {tooltipHeight}px
    </div>
  );
}

6. useTransition — Управление приоритетами обновлений

Позволяет помечать обновления состояния как несрочные, чтобы не блокировать интерфейс:

// Пример с поиском с задержкой
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>
  );
}

7. useId — Генерация уникальных ID

Генерирует стабильные уникальные 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>;
}

Когда использовать каждый хук

useState vs useReducer

  • useState — для простого состояния (строки, числа, булевы значения)
  • useReducer — для сложного состояния с множеством действий

useEffect vs useLayoutEffect

  • useEffect — для асинхронных операций (подписки, таймеры)
  • useLayoutEffect — когда нужно выполнить код до отрисовки браузером

useCallback vs useMemo

  • useCallback — для мемоизации функций
  • useMemo — для мемоизации значений

Распространённые ошибки

1. Избыточное использование мемоизации

// ❌ Не нужно мемоизировать простые значения
const value = useMemo(() => 5, []); // Ненужно!
 
// ✅ Просто используйте значение напрямую
const value = 5;

2. Забытые зависимости в useEffect

// ❌ Пропущена зависимость
useEffect(() => {
  console.log(userId); // userId используется, но не в зависимостях
}, []);
 
// ✅ Все зависимости указаны
useEffect(() => {
  console.log(userId);
}, [userId]);

3. Мутирование состояния напрямую

// ❌ Мутирование массива
const [items, setItems] = useState([1, 2, 3]);
items.push(4); // Не будет работать!
 
// ✅ Создание нового массива
const [items, setItems] = useState([1, 2, 3]);
setItems([...items, 4]); // Правильно!

Лучшие практики

1. Используйте правильные хуки для задач

// Хорошо: используем правильные хуки
function Timer() {
  const [seconds, setSeconds] = useState(0);
  
  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);
    
    return () => clearInterval(interval);
  }, []);
  
  return <div>Прошло: {seconds} секунд</div>;
}

2. Создавайте пользовательские хуки для повторяющейся логики

// Пользовательский хук для работы с локальным хранилищем
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];
}

3. Соблюдайте порядок зависимостей

// Хорошо: зависимости соответствуют использованию
useEffect(() => {
  if (userId) {
    fetchUser(userId).then(setUser);
  }
}, [userId]); // userId используется, значит должен быть в зависимостях

Резюме

React Hooks — это мощный инструмент для работы с состоянием и другими возможностями в функциональных компонентах:

Основные хуки:

  • useState — для простого состояния
  • useEffect — для побочных эффектов
  • useContext — для работы с контекстом

Дополнительные хуки:

  • useReducer — для сложного состояния
  • useCallback/useMemo — для оптимизации
  • useRef — для доступа к DOM и хранения значений
  • useTransition — для управления приоритетами обновлений
  • useId — для генерации уникальных ID

Пользовательские хуки:

  • Позволяют выносить повторяющуюся логику
  • Делают код более читаемым и переиспользуемым

Понимание всех хуков и умение применять их правильно — ключ к эффективной разработке на React! 🎯


Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪