Почему в setState() следует передавать функцию?

👨‍💻 Frontend Developer 🟡 Часто попадается 🎚️ Средний
#React #Асинхронность

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

В setState() передают функцию, чтобы получить актуальное состояние на момент изменения 🔄 Это важно из‑за асинхронности работы setState!

// ❌ Может сломаться:
setState({count: state.count + 1});
 
// ✅ Правильно:
setState(prevState => ({count: prevState.count + 1}));
setState(prev => ({count: prev.count + 1})); // 0 + 1 = 1
 

Полный ответ

Передача функции в setState() — как попросить друга запомнить, что именно нужно купить, а не писать список заново! 🛒

Проблема с обычным setState

React обновляет состояние асинхронно — не сразу:

// ❌ Проблема:
function handleClick() {
  // Представим, что count = 0
  setState({count: state.count + 1}); // 0 + 1 = 1
  setState({count: state.count + 1}); // 0 + 1 = 1 (опять!)
  setState({count: state.count + 1}); // 0 + 1 = 1 (и снова!)
  
  // Результат: count = 1, а не 3!
}

Почему так происходит:

  1. React собирает несколько setState() в очередь
  2. Выполняет их всех вместе позже
  3. state.count во всех вызовах один и тот же!

Решение с функцией

Функция получает актуальное предыдущее состояние:

// ✅ Решение:
function handleClick() {
  setState(prev => ({count: prev.count + 1})); // 0 + 1 = 1
  setState(prev => ({count: prev.count + 1})); // 1 + 1 = 2
  setState(prev => ({count: prev.count + 1})); // 2 + 1 = 3
  
  // Результат: count = 3 ✅
}

Как это работает:

  1. Каждая функция получает свежее prevState
  2. prevState всегда актуальное на момент вызова
  3. Результаты накапливаются правильно

Когда обязательно нужна функция

Множественные обновления

// ✅ Нужно для нескольких изменений подряд
const increment = () => {
  setState(prev => ({count: prev.count + 1}));
  setState(prev => ({count: prev.count + 1}));
  setState(prev => ({count: prev.count + 1}));
};

Зависимость от предыдущего состояния

// ✅ Когда новое состояние зависит от старого
const toggle = () => {
  setState(prev => ({isOpen: !prev.isOpen}));
};
 
const addItem = () => {
  setState(prev => ({
    items: [...prev.items, newItem]
  }));
};

Когда можно без функции

Полная замена состояния

// ✅ Можно без функции, если полностью заменяем
setState({name: 'Новое имя'});
setState({age: 25});
setState({isLoggedIn: true});

Когда не зависит от предыдущего значения

// ✅ Не зависит от предыдущего состояния
const resetForm = () => {
  setState({
    name: '',
    email: '',
    message: ''
  });
};

Частые ошибки

Игнорировать асинхронность

// ❌ Ошибка — думать, что setState синхронный
const handleClick = () => {
  setState({count: state.count + 1});
  console.log(state.count); // Старое значение!
};
 
// ✅ Правильно — использовать useEffect или callback

Смешивать подходы

// ❌ Ошибка — смешивать в одном обработчике
const handleClick = () => {
  setState({count: state.count + 1});        // ❌
  setState(prev => ({count: prev.count + 1})); // ✅
  setState({count: state.count + 1});        // ❌
};

Простые правила

  1. Функция = актуальное состояние 🔄
  2. Без функции = может быть старое состояние ⚠️
  3. Множественные обновления — всегда функция! 📈
  4. Полная замена — можно без функции 🔄
  5. Зависимость от предыдущего — нужна функция! 🔗
  6. setState асинхронный — помни об этом! ⏱️

Передача функции в setState() — как получать свежую информацию перед принятием решений! 💡


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