webroad.online
  1. 1Веб
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Инструменты
  8. 8React
  9. 9Стейт-менеджмент
  10. 10Next.js
  11. 11Формы
  12. 12Данные и бэкенд
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Анимации
  16. 16Тестирование
  17. 17Архитектура
React · Урок 2 из 7

Состояние, производные значения и reducer

Правильный useState, иммутабельные обновления, производное состояние и useReducer.

Обновлено

Что такое состояние

Состояние (state) = память компонента: данные, которые меняются со временем из-за взаимодействия (поле ввода, переключатель, счётчик). Когда состояние меняется, React перерисовывает компонент с новым значением.

Почему не обычная переменная? Локальная переменная (let count = 0) сбрасывается при каждом рендере, и её изменение не вызывает обновления экрана.

const [count, setCount] = useState(0)
  • countтекущее значение
  • setCountфункция, которая его меняет и запрашивает перерисовку
  • 0начальное значение

Props и state

Props State
Приходит от родителя самого компонента
Можно менять внутри компонента нет да, через сеттер
Роль настройка снаружи внутренняя память

Как работает сеттер

setCount(count + 1)        // новое значение
setCount(c => c + 1)       // функция-обновитель — получает самое свежее значение
  • Сеттер не меняет переменную сразу; он планирует новый рендер. console.log(count) сразу после покажет старое значение.
  • React группирует несколько обновлений (batching) в один рендер.
  • Когда новое значение зависит от старого → функция-обновитель: setCount(c => c + 1) три раза даёт +3; setCount(count + 1) три раза даёт +1.

Состояние иммутабельно

React сравнивает старое и новое состояние по ссылке. Объекты и массивы заменяют, а не изменяют — см. Ссылки и иммутабельность.

setUser({ ...user, name })                       // ✓
setTodos(todos => [...todos, newTodo])           // ✓
user.name = name; setUser(user)                  // ✗ та же ссылка → перерисовки нет

Производное состояние — не храните его

Если значение можно вычислить из props или состояния, вычисляйте его при рендере. Не держите его в отдельном useState.

// ✗ два источника истины + эффект, который их синхронизирует
const [todos, setTodos] = useState([])
const [doneCount, setDoneCount] = useState(0)

// ✓ один источник, остальное выводится
const doneCount = todos.filter(t => t.done).length

Действительно дорогие вычисления → useMemo (или React Compiler, который мемоизирует автоматически).

Где хранить состояние — варианты и когда

Где Когда Пример
локальный useState нужен одному компоненту поле ввода, открытый выпадающий список
поднятое в родителя (lift state up) нужно двум соседям фильтр + список
URL (?page=2) нужно делиться ссылкой и пережить перезагрузку фильтры, пагинация, активная вкладка
Context много глубоких компонентов, редкие изменения тема, вошедший пользователь, язык
стор (Zustand) глобальное, меняется часто корзина в SPA
кэш серверного состояния данные с сервера Серверное состояние

Правило: как можно ниже, поднимаете только при необходимости.

useReducer — когда логика растёт

Когда у состояния много способов измениться, логику собирают в reducer — чистую функцию (state, action) => newState:

function todosReducer(state, action) {
  switch (action.type) {
    case 'add': return [...state, action.todo]
    case 'toggle': return state.map(t => (t.id === action.id ? { ...t, done: !t.done } : t))
    case 'remove': return state.filter(t => t.id !== action.id)
    default: return state
  }
}

const [todos, dispatch] = useReducer(todosReducer, [])
dispatch({ type: 'add', todo })
useState useReducer
1–3 простых значения состояние с множеством переходов
логика в обработчиках логика в одном месте, тестируется отдельно

Коротко

  • Состояние = память компонента; сеттер запрашивает перерисовку.
  • Функция-обновитель, когда зависите от старого значения; всегда новые объекты.
  • Выводите то, что можно вычислить; держите состояние как можно ниже; URL — для того, чем нужно делиться.

Официальные источники

Упражнения

Была ли страница полезной?

Один клик — без регистрации.