Проблема почти всегда во фронтенде
Проблема почти всегда во фронтенде

Кейс React: как работает сканер штрихкода и как я его чинил в React-витрине

1. Что такое “витрина” на кассе

Если отбросить бизнес-термины, витрина терминала — это просто:

❗веб-страница (URL), открытая в браузере терминала

Обычно это:

https://terminal.company.com

По сути:

React-приложение + API + браузер

Кассир видит интерфейс, но под капотом — обычный фронтенд:

DOM
JavaScript
React
Redux (иногда)

2. Как на самом деле работает сканер штрихкода

Основная идея:

❗ Сканер — это НЕ “магия”, это клавиатура

Он работает как:

input device → как keyboard

Что происходит при сканировании

  1. Сканер “печатает” цифры:

4 8 2 9 3 8 1 2 3 4

Enter

Итоговая логика

цифры → собираются в строку (buffer)

↓

Enter

↓

поиск товара

↓

если найден:

- добавить в корзину

- или увеличить количество (+1)

3. Что пошло не так

не работает сканер штрихкода на кассе

У нас была новая витрина (React), и сканер не работал на двух разных кассах.

Это сразу намекнуло:

❗проблема не в железе, а во фронте

Дальше - отладка.

4. Проблемы, которые я нашел

❌ 1. Поиск не работал по штрихкоду

Работало:

поиск по названию — OK

поиск по barcode — НЕ работает

Причина:

product.title.includes(...)

но не было:

product.barcode ===input

❌ 2. Штрихкод не собирался в буфер

Сканер отправляет поток символов.

Нужно было:

let barcodeBuffer="";

Но логика сбрасывала буфер неправильно.

Ошибка:

каждый символ перезаписывал предыдущий

Решение:

barcodeBuffer += e.key;

❌ 3. Нет реакции на Enter

Даже когда буфер заполнялся:

"4829381234"

ничего не происходило.

Не было обработки:

if (e.key ==="Enter") {

// логика поиска

}

❌ 4. Обработчик создавался несколько раз

Самая неприятная ошибка.

React-компонент ререндерится → useEffect может сработать несколько раз.

В итоге:

1 скан → 2–3 добавления товара

Причина:

document.addEventListener("keydown", handler);

вызывался несколько раз.

✅ Решение

useEffect(() => {

consthandler= (e) => { ... };

document.addEventListener("keydown", handler);

return () => {

document.removeEventListener("keydown", handler);

};

}, [ ]);

Ключевое:

[ ] → выполнить один раз

removeEventListener → удалить обработчик

❌ 5. Потеря актуального каталога

Обработчик не видел актуального каталога.

Решение:

const catalogRef=useRef();

useEffect(() => {

catalogRef.current =localCatalog;

}, [localCatalog]);

И использовать:

catalogRef.current

5. Итоговая логика сканера

let barcodeBuffer="";

let lastKeyTime=0;

const handleKeyDown= (e) => {

const now=Date.now();

if (now - lastKeyTime>100) {

barcodeBuffer="";

}

lastKeyTime=now;

if (e.key ==="Enter") {

if (barcodeBuffer.length >=6) {

const product=catalogRef.current?.products?.find(p => p.barcode ===barcodeBuffer );

if (product) {

dispatch(addProduct(product));

}

}

barcodeBuffer="";

return;

}

if (e.key.length ===1) {

barcodeBuffer += e.key;

}

};

6. Как правильно отлаживать такие вещи

Очень важный вывод.

Шаг 1 — проверить backend

приходит ли запрос после сканирования?

Если да → проблема не в сканере

Если нет → идём на фронт

Шаг 2 — проверить фронт

Смотреть:

console.log(e.key)

Понимаем:

приходят ли вообще символы

Шаг 3 — проверить buffer

собирается ли строка

Шаг 4 — проверить Enter

срабатывает ли финальное событие

Шаг 5 — проверить реакцию на событие

dispatch(addProduct)

7. Главный вывод

Сканер — это не “устройство”, а:

поток клавиатурных событий

И если он “не работает” — почти всегда проблема:

в обработке этих событий на фронте

Финал

Этот кейс показал:

железо почти никогда не виновато

Если:

сканер не работает на разных терминалах

→ значит проблема:

в коде витрины