Самый тупой баг Это баг, над которым я буду угорать еще долго | 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 самостоятельно прав нет.
Парам-парам-пам. Конец.