Avatar ·

Что означают три точки в React? Объясните оператор расширения (spread syntax)

Привет! Я начал изучать React и часто вижу в примерах кода странные три точки перед объектами или массивами. Например: ```javascript const newObject = { ...oldObject, newKey: 'value' }; ``` Или в JSX: ```jsx const props = { name: 'John', age: 30 }; ``` Я понимаю, что это как-то связано с копированием данных, но не до конца разбираюсь. Когда я пытаюсь сделать похожий код без этих точек, у меня всё ломается. Например, я хотел объединить два объекта: ```javascript const user = { name: 'Alice' }; const details = { age: 25, city: 'Moscow' }; const merged = user + details; // Ошибка! ``` Или скопировать массив: ```javascript const arr1 = [1, 2, 3]; const arr2 = arr1; // Но это ссылка, а не копия! arr2.push(4); console.log(arr1); // [1, 2, 3, 4] — не то, что я ожидал ``` Подскажите, что это за три точки? Как они работают в JavaScript и React? Почему они так популярны? И как правильно копировать объекты и массивы без мутации исходных данных? <<>>

Что такое три точки (spread syntax) в JavaScript?

Три точки ... — это оператор расширения (spread operator), появившийся в ES6 (ES2015). Он позволяет «развернуть» элементы итерируемого объекта (массива, строки, объекта) в места, где ожидается несколько элементов или свойств. В React он используется постоянно для удобной работы с пропсами, состоянием и неизменяемостью данных.

Основные варианты использования

1. Копирование массивов

Вместо создания ссылки (как в вашем примере с arr2 = arr1) используйте spread:

const original = [1, 2, 3];
const copy = [...original]; // Создаётся новый массив
copy.push(4);
console.log(original); // [1, 2, 3] — неизменён
console.log(copy);      // [1, 2, 3, 4]

Это поверхностное копирование (shallow copy) — для вложенных объектов нужно быть осторожным.

2. Объединение массивов

const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]

3. Копирование и объединение объектов

const user = { name: 'Alice' };
const details = { age: 25, city: 'Moscow' };
const merged = { ...user, ...details };
// { name: 'Alice', age: 25, city: 'Moscow' }

Важно: если ключи совпадают, последний объект перезаписывает значения:

const a = { x: 1, y: 2 };
const b = { y: 3, z: 4 };
const result = { ...a, ...b }; // { x: 1, y: 3, z: 4 }

4. Добавление/перезапись свойств

const base = { name: 'John', age: 30 };
const updated = { ...base, age: 31, city: 'NY' };
// { name: 'John', age: 31, city: 'NY' }

Как это работает в React?

Передача пропсов

Самый частый кейс — распыление объекта пропсов в JSX:

// Вместо:
<MyComponent name={props.name} age={props.age} city={props.city} />

// Можно:
<MyComponent {...props} />

Это эквивалентно передаче каждого свойства отдельно.

Обновление состояния (useState)

В React важно не мутировать состояние напрямую:

// ❌ НЕПРАВИЛЬНО (мутация):
setUser(user.name = 'Bob');

// ✅ ПРАВИЛЬНО (создаём новый объект):
setUser({ ...user, name: 'Bob' });

Работа с массивами в состоянии

// Добавление элемента:
setItems([...items, newItem]);

// Удаление элемента (по индексу):
setItems(items.filter((_, i) => i !== index));

// Обновление элемента:
setItems(items.map((item, i) => 
  i === index ? { ...item, done: true } : item
));

Важные ограничения

  • Поверхностное копирование: вложенные объекты копируются по ссылке. Для глубокого копирования используйте structuredClone() или библиотеки (Immer, Lodash).
  • Не работает с примитивами: ...5 вызовет ошибку, так как число не итерируемо.
  • Порядок важен: при объединении объектов последний перезаписывает предыдущие одинаковые ключи.

Альтернативы

До ES6 использовали Object.assign():

const merged = Object.assign({}, user, details);

Но spread-синтаксис короче и читаемее.

Итог

Три точки — это мощный инструмент для работы с неизменяемостью данных. В React он помогает обновлять состояние без мутации, удобно передавать пропсы и копировать объекты/массивы. Запомните главное правило: ... создаёт новый объект/массив, а не ссылку на старый.

<<>>

Дополнение: оператор rest (оставшиеся параметры)

Важно не путать spread-синтаксис с очень похожим, но противоположным по смыслу rest-оператором. Тот же символ ... используется для сбора оставшихся элементов в массив или объект.

Rest в деструктуризации массивов

const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first);  // 1
console.log(second); // 2
console.log(rest);   // [3, 4, 5]

Rest в деструктуризации объектов

const { name, ...details } = { name: 'Alice', age: 25, city: 'Moscow' };
console.log(name);    // Alice
console.log(details); // { age: 25, city: 'Moscow' }

Rest в параметрах функции

function sum(...numbers) {
  return numbers.reduce((acc, n) => acc + n, 0);
}
sum(1, 2, 3, 4); // 10

Как запомнить разницу?

  • Spread — «разворачивает» массив/объект на отдельные элементы (используется при создании новых данных).
  • Rest — «собирает» отдельные элементы в массив/объект (используется при получении данных).

Визуально: spread — из одного делает много, rest — из много делает одно.

Практический совет для React

Часто сочетают оба оператора при работе с пропсами:

function MyComponent({ name, ...otherProps }) {
  // name — отдельно, остальные пропсы — в otherProps
  return <div {...otherProps}>Привет, {name}!</div>;
}

Это позволяет явно выделить нужные пропсы и передать остальные дальше.