Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. JavaScript: ссылки, коллекции и состояние React
js_state_foundations

JavaScript: ссылки, коллекции и состояние React

Ссылочная семантика, поверхностное копирование, неизменяемые операции с массивами и безопасное обновление вложенных данных.

JavaScript: ссылки, коллекции и состояние React

Разберём поведение JavaScript, из-за которого изменения состояния в React чаще всего дают неожиданный результат.

#Что сделаем

Возьмём список товаров и добавим выбор одного элемента. По ходу работы выясним, почему копии через spread иногда недостаточно, какие методы массива меняют исходные данные и зачем React нужны новые ссылки на изменившиеся объекты.

Весь код остаётся на TypeScript, но в этом уроке нас интересует именно поведение JavaScript во время выполнения.

#1. Объект хранится по ссылке

Число или строка копируются как самостоятельные значения. С переменными, которые указывают на объект, всё иначе: две переменные могут вести к одному объекту.

const product = { id: 'p1', selected: false }; const sameProduct = product; sameProduct.selected = true; console.log(product.selected); // true

Нового товара здесь не появилось. Мы изменили единственный объект через вторую ссылку на него.

React сравнивает старое и новое состояние по ссылкам. Если изменить старый объект на месте, предыдущий снимок состояния тоже окажется испорчен, а React не получит надёжного признака изменения.

Проверьте себя. Добавьте console.log(product === sameProduct). Результат true подтверждает, что обе переменные указывают на один объект.

#2. Spread делает только поверхностную копию

У товара появилась вложенная настройка:

type Item = { id: string; meta: { selected: boolean }; }; const items: Item[] = [ { id: 'p1', meta: { selected: false } }, ]; const copy = [...items]; copy[0].meta.selected = true;

Массив copy новый, но copy[0] и items[0] всё ещё указывают на один товар. Вложенный meta также остался общим. Поэтому items[0].meta.selected неожиданно стал true.

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

function select(items: readonly Item[], id: string): Item[] { return items.map((item) => item.id === id ? { ...item, meta: { ...item.meta, selected: true } } : item, ); }

Неизменившиеся товары можно оставить прежними. Новый объект нужен только выбранному товару и его изменившемуся meta.

Проверьте себя. Убедитесь тестом, что новый массив отличается от исходного, выбранный товар получил новую ссылку, а остальные товары сохранили прежние ссылки.

#3. Методы массива: какие меняют исходник

map, filter, slice и toSorted возвращают новый массив. push, splice, sort и reverse меняют существующий.

const visible = items.filter((item) => item.meta.selected); const ordered = items.toSorted((a, b) => a.id.localeCompare(b.id));

visible и ordered можно вычислить из исходных данных во время отрисовки. Не стоит записывать их в отдельное состояние: иначе придётся вручную синхронизировать несколько копий одной информации.

Если старый браузер не поддерживает toSorted, используйте [...items].sort(...): сначала создайте новый массив и только затем сортируйте его.

Частая ошибка. Вызвать items.sort(...) для массива из состояния. Порядок изменится и в старом снимке.

#4. Переменная модуля общая для всех потребителей

Код верхнего уровня модуля выполняется один раз при загрузке. Изменяемая переменная такого модуля становится общей для всех компонентов, которые его импортировали.

let selectedId: string | null = null; export function selectProduct(id: string) { selectedId = id; }

Это не локальное состояние компонента. Две открытые карточки будут менять одну переменную, а React не узнает о её изменении и не запустит новую отрисовку.

В модуле безопасно хранить неизменяемые настройки и чистые функции. Пользовательское состояние должно принадлежать компоненту, внешнему хранилищу с подпиской или серверу — выбор зависит от области его жизни.

#5. Проверка результата

Для функции select полезны четыре проверки:

  1. выбранный товар получил selected: true;
  2. исходный массив не изменился;
  3. вложенный объект выбранного товара скопирован;
  4. остальные товары не копируются без причины.
const source: Item[] = [ { id: 'p1', meta: { selected: false } }, { id: 'p2', meta: { selected: false } }, ]; const result = select(source, 'p1'); expect(result).not.toBe(source); expect(result[0]).not.toBe(source[0]); expect(result[0].meta).not.toBe(source[0].meta); expect(result[1]).toBe(source[1]); expect(source[0].meta.selected).toBe(false);

Здесь проверяется не способ написания функции, а важное для React поведение: старый снимок остаётся целым, изменённая ветвь получает новые ссылки, неизменённая не копируется.

#Что запомнить

  • Копия массива не копирует его элементы.
  • Копируйте весь путь до изменяемого вложенного поля.
  • Не записывайте в состояние то, что можно вычислить из других данных.
  • Не храните пользовательское состояние в обычной переменной модуля.

Следующий урок применит эти правила к данным неизвестной формы, полученным из API.

#Источники для сверки

  • MDN: работа с объектами
  • MDN: методы Array
  • React: обновление объектов в состоянии
  • React: обновление массивов в состоянии

Устранение неисправностей

Проверьте свои знания

Вопросы ещё не добавлены

Вопросы для этой подтемы ещё не добавлены.

Далее: TypeScript: данные из API без any