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

Как useState сохраняет предыдущее состояние?

2.0 Middle🔥 261 комментариев
#JavaScript Core

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

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

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

Как useState сохраняет состояние в React

useState - это хук React, который позволяет функциональным компонентам иметь локальное состояние. Механизм его работы включает несколько интересных деталей.

Концепция: Closure и Fiber Architecture

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

1. Замыкание (Closure)

Каждый компонент имеет область видимости, в которой создаются переменные состояния:

function Counter() {
  // useState создаёт переменную, которая "запоминается"
  const [count, setCount] = React.useState(0);
  
  // count и setCount доступны благодаря замыканию
  // функция setCount может изменить count через замыкание
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

2. Fiber Architecture

React использует внутреннюю структуру "fiber" для каждого компонента:

// Упрощённое внутреннее представление (не реальный код React)
const ComponentFiber = {
  component: Counter,
  state: [
    { value: 0, queue: [] },  // Первый useState
    { value: 'John', queue: [] } // Второй useState
  ],
  hooks: [
    { memoizedState: 0, baseState: 0, ... },
    { memoizedState: 'John', baseState: 'John', ... }
  ]
};

Как это работает в деталях

Инициализация

При первом рендере компонента:

function UserProfile() {
  // При первом вызове: initialValue = 0
  const [count, setCount] = React.useState(0);
  // React создаёт fiber и сохраняет { value: 0 }
}

// React регистрирует это в своей памяти:
// fiber.state[0] = { value: 0, queue: [] }

Хранение значения

Когда вы вызываете useState, React возвращает текущее значение из памяти:

function Counter() {
  // React смотрит в fiber.state[0] и находит { value: 5 }
  // Возвращает: [5, setter]
  const [count, setCount] = React.useState(0);
  
  // count = 5 (из памяти React, не из параметра)
  console.log(count); // 5, а не 0!
}

Обновление состояния

Когда вы вызываете setCount:

const [count, setCount] = React.useState(0);

setCount(5);

// Что происходит внутри:
// 1. React добавляет обновление в очередь:
//    fiber.state[0].queue.push({ action: 5 })
// 2. React планирует новый render
// 3. При новом render React берёт новое значение из памяти
// 4. count теперь = 5 вместо 0

Идентификация setState с помощью порядка вызовов

React использует порядок вызовов useState для отслеживания, какое состояние какому относится:

function Profile() {
  const [name, setName] = useState('John');    // useState #1
  const [age, setAge] = useState(30);           // useState #2
  const [email, setEmail] = useState('j@e.com'); // useState #3
  
  // React запоминает:
  // hooks[0] = name
  // hooks[1] = age
  // hooks[2] = email
}

ВАЖНО: Порядок должен быть консистентным!

// ПЛОХО: нарушает порядок
function BadExample({ condition }) {
  if (condition) {
    const [name, setName] = useState('John'); // Может быть #1 или #2
  }
  const [age, setAge] = useState(30); // Может быть #2 или #1
}

// ХОРОШО: всегда одинаковый порядок
function GoodExample({ condition }) {
  const [name, setName] = useState('John');
  const [age, setAge] = useState(30);
  
  if (condition) {
    // Используем значения здесь
  }
}

Под капотом: как React хранит значения

1. React элемент и его fiber

const element = <Counter />;

// Каждому элементу соответствует fiber
// Fiber содержит:
// - Ссылку на функцию компонента
// - memoizedState (текущее состояние)
// - update queue (очередь обновлений)

2. Цепочка hooks (linked list)

React связывает все useState в список:

// Упрощённое представление
functionalComponent.memoizedState = {
  state: 'John',          // Значение первого useState
  queue: [],
  next: {
    state: 30,           // Значение второго useState
    queue: [],
    next: null
  }
};

3. Update queue (очередь обновлений)

function Counter() {
  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    setCount(count + 1); // #1
    setCount(count + 1); // #2
    setCount(count + 1); // #3
  };
  
  // Все три вызова добавляются в queue:
  // queue = [
  //   { action: count + 1 },
  //   { action: count + 1 },
  //   { action: count + 1 }
  // ]
  // 
  // При обработке React применяет их последовательно
}

Сохранение между рендерами

Каждый компонент получает свой fiber:

function App() {
  return (
    <>
      <Counter /> {/* Fiber #1 с собственным state */}
      <Counter /> {/* Fiber #2 с собственным state */}
    </>
  );
}

// Две разные переменные count в разных компонентах
// каждая сохраняется в своём fiber

Сохранение при условном рендере:

function ConditionalComponent() {
  const [count, setCount] = useState(0);
  const [isVisible, setIsVisible] = useState(true);
  
  if (!isVisible) {
    return <div>Hidden</div>;
  }
  
  // Даже если компонент вернул другой JSX,
  // React помнит count и другие состояния в fiber
  
  return <div>{count}</div>;
}

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

function FormComponent() {
  // Инициально React запоминает эти значения
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [email, setEmail] = useState('');
  
  // При каждом рендере React берёт значения из памяти
  // не из параметров useState
  
  return (
    <>
      <input
        value={firstName}
        onChange={(e) => setFirstName(e.target.value)}
      />
      <input
        value={lastName}
        onChange={(e) => setLastName(e.target.value)}
      />
      <input
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <p>Data: {firstName}, {lastName}, {email}</p>
    </>
  );
}

При вводе текста:

1. onChange -> setFirstName('J')
2. React обновляет fiber.state[0].value = 'J'
3. React перерендеривает компонент
4. useState('') возвращает 'J' из памяти
5. input.value = 'J' отображается

Почему это работает

  • Fiber Architecture - React хранит состояние привязано к каждому экземпляру компонента
  • Замыкание - setCount имеет доступ к переменным через closure
  • Консистентный порядок - React полагается на порядок вызовов
  • Связанный список - все useState в компоненте связаны в цепь

Этот механизм позволяет React автоматически сохранять состояние между рендерами и обновлять компонент при изменении этого состояния.

Как useState сохраняет предыдущее состояние? | PrepBro