Получи случайную криптовалюту за регистрацию!

Самый тупой баг Это баг, над которым я буду угорать еще долго | Code&Travel

Самый тупой баг

Это баг, над которым я буду угорать еще долго. Я потратила на него 8 часов. Привлекла дополнительного эксперта, чтобы найти причину. И когда обнаружила ее в итоге, не могла остановиться от смеха 10 минут.

Рассказываю ситуацию. У вас есть основной проект, и есть ui-kit. UI-kit - это еще один проект, в котором лежат ваши инпуты, селекты, таблицы и др. элементы интерфейса, которые постоянно повторяются. Нужен он для того, чтобы не стилизовать каждый раз инпут заново и не прописывать одну и ту же однотипную логику. Вы просто уже используете готовый кастомный инпут, который вы заранее определили.

Так вот, есть задача. У вас есть таблица с данными и кнопка «удалить запись» в каждой строке таблицы. Представьте себе таблицу ваших трат за месяц. В ячейках указаны характеристики каждой траты (на что пошли деньги, сумма, категория и др.). И в последней ячейке у каждой траты кнопка «Удалить».

Вы выбираете трату «апельсины 0.5 кг» и нажимаете кнопку «Удалить». А удаляется почему-то трата «консультация у врача». При этом трата «апельсины 0.5 кг» остается. И так со всеми тратами: удаляется не то, что нужно. Однако, иногда везет и удаляется, что нужно, причина везения не ясна. В чем же дело!?

Для диагностики бага важно, что ваша таблица «Траты» использует таблицу из ui-kit. В нее нужно передать заранее сформированные данные.

const dataForTable = dataFromBackend.map((item) => ({
id: item.id,
cells: getTableCellsInfo(item),
}));

При удалении элемента таблицы вы заново загружаете всю таблицу (запрашиваете информацию с backend). И в результате дебаггинга обнаруживается, что запрос на backend для удаления отправляется верный, а в новой таблице (которая пришла после удаления элемента) отсутствует не тот элемент, что вы удалили. И проблема 100% на вашей стороне, а не на стороне backend. Магия!

На самом деле, баг весьма популярен, если вы забыли указать key в функции map, когда формировали данные для таблицы. Напомню, как она выглядит:

todos.map((todo) => (

));
}

React просто не знает, элемент с каким id вы удалили, считает его по порядковому номеру, а поскольку с backend данные таблицы приходят без сортировки и каждый раз разные, баг и возникает. Попытка отсортировать данные с backend не увенчалась успехом. Предположим, вы удаляете элемент из середины: 3-ий. Тогда следующий за ним, 4-ый, уже перестает быть таковым и становится 3-им. И у вас опять все на фронте едет.

Об этой проблеме было описано здесь и объяснено, почему нехорошо использовать порядковые индексы в качестве key вместо id.

Вот так делать не надо:

todos.map((todo, index) => (

));
}

А вот так - пожалуйста:

todos.map((todo) => (

));

Но меня-то эта проблема не касается! Здесь четко указано, какой id с какой ячейкой таблицы соотносить:

const dataForTable = dataFromBackend.map((item) => ({
id: item.id,
cells: getTableCellsInfo(item),
}));

Но… нет.

Оказывается, как-то раз один разработчик делал рефакторинг компонента таблицы в ui-kit. И именно в ui-kit и используется функция map, о которой говорилось выше. Раньше логика компонента таблицы выглядела так:

todos.map((todo) => (

));

И много где еще внутри компонента использовалось todo.rowId. А потом разработчик решил порефакторить и поменял todo.rowId на todo.id. Только вот в половине кода забыл (видимо, отошел по делам). В том числе в месте, связанном с функцией map.

Т.е. теперь внутри компонента ui-kit код попеременно обращается, то к todo.id, то к todo.rowId. И для работоспособности кода нужно передавать оба идентификатора: и id, и rowId.

Т.е. теперь уже ваш говнокод будет выглядеть так:

const dataForTable = dataFromBackend.map((item) => ({
id: item.id,
rowId: item.id,
cells: getTableCellsInfo(item),
}));

Шикарно…

P. S.: редактировать ui-kit самостоятельно прав нет.

Парам-парам-пам. Конец.