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

В чем разница между useState и useCallback?

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

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

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

Разница между useState и useCallback

useState и useCallback — это два важных хука React, которые служат совершенно разным целям и работают на разных уровнях управления состоянием и производительностью.

Определение

useState — это хук для управления состоянием компонента. Он позволяет функциональному компоненту иметь локальное состояние, которое будет сохраняться между рендерами.

useCallback — это хук для оптимизации производительности, который мемоизирует функцию и возвращает ее стабильную ссылку. Это предотвращает ненужные рендеры дочерних компонентов.

Основные различия

1. Назначение

useState управляет данными компонента:

const [count, setCount] = useState(0);
const [name, setName] = useState('');

// Состояние изменяется, компонент перерендеривается
setCount(count + 1);

useCallback управляет функциями и их стабильностью:

const handleClick = useCallback(() => {
  console.log('Clicked');
}, []); // Функция мемоизируется

2. Когда срабатывает

useState срабатывает при каждом вызове setter функции:

function Counter() {
  const [count, setCount] = useState(0);
  
  const increment = () => {
    setCount(count + 1); // Вызов setter -> перерендер
  };
  
  return <button onClick={increment}>{count}</button>;
}

useCallback срабатывает при изменении зависимостей:

function Parent() {
  const [count, setCount] = useState(0);
  
  // handleClick пересоздается только если count изменяется
  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);
  
  return <Child onClick={handleClick} />;
}

Практические примеры

Пример 1: useState для управления состоянием

import { useState } from 'react';

function TodoList() {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState('');
  
  const addTodo = () => {
    setTodos([...todos, { id: Date.now(), text: input }]);
    setInput(''); // Очистить input
  };
  
  return (
    <div>
      <input 
        value={input} 
        onChange={(e) => setInput(e.target.value)} 
      />
      <button onClick={addTodo}>Add</button>
      <ul>
        {todos.map(todo => <li key={todo.id}>{todo.text}</li>)}
      </ul>
    </div>
  );
}

Пример 2: useCallback для оптимизации

import { useState, useCallback } from 'react';

// Дочерний компонент, оптимизированный через React.memo
const Child = React.memo(({ onClick }) => {
  console.log('Child rendered');
  return <button onClick={onClick}>Click me</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');
  
  // БЕЗ useCallback - Child перерендеривается при каждом изменении name
  // const handleClick = () => setCount(count + 1);
  
  // С useCallback - Child не перерендеривается, если count не меняется
  const handleClick = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);
  
  return (
    <div>
      <input 
        value={name} 
        onChange={(e) => setName(e.target.value)} 
      />
      <p>Count: {count}</p>
      <Child onClick={handleClick} />
    </div>
  );
}

Пример 3: useState + useCallback вместе

import { useState, useCallback } from 'react';

function SearchUsers() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(false);
  
  // useCallback оборачивает функцию, которая обновляет состояние
  const handleSearch = useCallback(async (searchTerm) => {
    if (!searchTerm) {
      setResults([]);
      return;
    }
    
    setLoading(true);
    try {
      const data = await fetch(`/api/search?q=${searchTerm}`);
      const users = await data.json();
      setResults(users);
    } finally {
      setLoading(false);
    }
  }, []); // Зависимостей нет, функция стабильна
  
  return (
    <div>
      <input 
        value={query}
        onChange={(e) => {
          setQuery(e.target.value);
          handleSearch(e.target.value);
        }}
      />
      {loading && <p>Loading...</p>}
      <ul>
        {results.map(user => <li key={user.id}>{user.name}</li>)}
      </ul>
    </div>
  );
}

Когда использовать

useState используется когда:

  • Нужно хранить и обновлять данные компонента
  • Изменение данных должно вызывать перерендер
  • Нужна реактивность на пользовательский ввод

useCallback используется когда:

  • Передаешь функцию в оптимизированные дочерние компоненты (React.memo)
  • Функция является зависимостью других хуков (useEffect, useMemo)
  • Хочешь избежать ненужных рендеров

Важные замечания

// useCallback не должен заменять useState
const [count, setCount] = useState(0);

// Неправильно - это не заменит useState
const handleIncrement = useCallback(() => {
  setCount(count + 1);
}, [count]);

// useCallback здесь просто стабилизирует функцию,
// не более того

Вывод

useState отвечает за данные и реактивность компонента. useCallback отвечает за производительность и стабильность ссылок на функции. Это совершенно разные инструменты для разных задач. Используй useState для управления состоянием, а useCallback для оптимизации производительности при передаче функций в дочерние компоненты.