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

Немного предыстории проблемы. Я занимался рефакторингом виджета на нашем сайте, используемого для создания пользователей, который включает компонент Selection для выбора команд для этого пользователя. Команды сохранялись как Set team_id в пользовательском объекте, который включал другие поля, такие как firstName, >фамилия, адрес электронной почты и язык. Эти пользовательские объекты хранятся в массиве с именем members, который сохраняется в состоянии компонента.

Возникшая ошибка возникает после того, как клиент отправляет запрос на создание пользователей, а затем пытается использовать компонент Selection для выбора команды.

team_id.add is not a function

Нани? team_id был Set минуту назад… что случилось? После размещения нескольких журналов консоли я обнаружил, что в какой-то момент team_id был преобразован в массив. Время исследовать.

Вскоре я нашел фрагмент кода, который обрабатывает members в полезную нагрузку, готовую для серверной части непосредственно перед запросом. Наряду с установкой значений по умолчанию для пустых полей была одна строка, которая преобразовывала Set team_id в массив.

membersAdded[i].team_id = […membersAdded[i].team_id];

Когда я впервые нашел эту строку, я решил, что membersAdded должна указывать на ту же ссылку в элементах состояния компонента. Все, что мне нужно было сделать, это создать новую ссылку с тем же массивом, используя оператор распространения, верно?

const { members } = this.state; 
const membersAdded = […members];

Итак, теперь у нас есть совершенно новый массив membersAdded. Ура! Работы закончены!

К сожалению, это не так. Ошибка осталась, и когда я установил отладчик сразу после строки, которая преобразовывала team_id в массив.я проверил обе переменные members и membersAdded и увидел, что team_id был изменен на массив для обеих! Что дает? Это должен быть новый массив…

После поиска в Google некоторых статей и обнаружения этой заметки на MDN:

“Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays”

Я узнал, что оператор распространения только shallow клонирует массив, поэтому ссылки, которые у меня были в пользовательском объекте, на team_id в новом массиве MemberAdded по-прежнему ссылались на один и тот же team_id от участников. Решением, предложенным для выполнения глубокого клонирования, был внешний модуль lodash/cloneDeep, который, мол, сделал свою работу!

const membersAdded = cloneDeep(members);

После этого ошибки больше не было, и исходный team_id в members остался Set!

Спасибо за чтение.