es
Feedback
Frontender's notes [ru]

Frontender's notes [ru]

Ir al canal en Telegram

Ведущий канал о современном фронтенде: статьи, новости, практики, вайбкодинг и автоматизация фронта ИИ-агентами. Личный блог автора - @just_genych По вопросам рекламы или разработки - @g_abashkin

Mostrar más

📈 Análisis del canal de Telegram Frontender's notes [ru]

El canal Frontender's notes [ru] (@frontendnoteschannel_ru) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 32 236 suscriptores, ocupando la posición 4 192 en la categoría Tecnologías y Aplicaciones y el puesto 20 089 en la región Rusia.

📊 Métricas de audiencia y dinámica

Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 32 236 suscriptores.

Según los últimos datos del 03 julio, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de -332, y en las últimas 24 horas de -13, conservando un alto alcance.

  • Estado de verificación: No verificado
  • Tasa de interacción (ER): El promedio de interacción de la audiencia es 7.14%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 4.60% de reacciones respecto al total de suscriptores.
  • Alcance de las publicaciones: Cada publicación recibe en promedio 2 303 visualizaciones. En el primer día suele acumular 1 483 visualizaciones.
  • Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 10.
  • Intereses temáticos: El contenido se centra en temas clave como браузер, api, css, интерфейс, загрузка.

📝 Descripción y política de contenido

El autor describe el recurso como un espacio para expresar opiniones subjetivas:
Ведущий канал о современном фронтенде: статьи, новости, практики, вайбкодинг и автоматизация фронта ИИ-агентами. Личный блог автора - @just_genych По вопросам рекламы или разработки - @g_abashkin

Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 04 julio, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.

32 236
Suscriptores
-1324 horas
-827 días
-33230 días
Atraer Suscriptores
julio '26
julio '26
+5
en 0 canales
junio '26
+111
en 0 canales
Get PRO
mayo '26
+239
en 22 canales
Get PRO
abril '26
+300
en 0 canales
Get PRO
marzo '26
+136
en 1 canales
Get PRO
febrero '26
+156
en 1 canales
Get PRO
enero '26
+250
en 60 canales
Get PRO
diciembre '25
+319
en 28 canales
Get PRO
noviembre '25
+379
en 69 canales
Get PRO
octubre '25
+210
en 32 canales
Get PRO
septiembre '25
+265
en 90 canales
Get PRO
agosto '25
+111
en 6 canales
Get PRO
julio '25
+223
en 2 canales
Get PRO
junio '25
+247
en 9 canales
Get PRO
mayo '25
+255
en 4 canales
Get PRO
abril '25
+266
en 1 canales
Get PRO
marzo '25
+285
en 24 canales
Get PRO
febrero '25
+449
en 27 canales
Get PRO
enero '25
+380
en 36 canales
Get PRO
diciembre '24
+612
en 53 canales
Get PRO
noviembre '24
+576
en 40 canales
Get PRO
octubre '24
+439
en 27 canales
Get PRO
septiembre '24
+470
en 34 canales
Get PRO
agosto '24
+466
en 24 canales
Get PRO
julio '24
+458
en 34 canales
Get PRO
junio '24
+431
en 33 canales
Get PRO
mayo '24
+442
en 30 canales
Get PRO
abril '24
+371
en 26 canales
Get PRO
marzo '24
+374
en 13 canales
Get PRO
febrero '24
+846
en 4 canales
Get PRO
enero '24
+590
en 22 canales
Get PRO
diciembre '23
+786
en 34 canales
Get PRO
noviembre '23
+431
en 8 canales
Get PRO
octubre '23
+522
en 1 canales
Get PRO
septiembre '23
+842
en 0 canales
Get PRO
agosto '23
+1 001
en 0 canales
Get PRO
julio '23
+1 476
en 0 canales
Get PRO
junio '23
+1 096
en 0 canales
Get PRO
mayo '23
+916
en 0 canales
Get PRO
abril '23
+908
en 0 canales
Get PRO
marzo '23
+878
en 0 canales
Get PRO
febrero '23
+584
en 0 canales
Get PRO
enero '23
+1 454
en 0 canales
Get PRO
diciembre '22
+638
en 0 canales
Get PRO
noviembre '22
+1 656
en 0 canales
Get PRO
octubre '22
+536
en 0 canales
Get PRO
septiembre '22
+537
en 0 canales
Get PRO
agosto '22
+857
en 0 canales
Get PRO
julio '22
+1 735
en 0 canales
Get PRO
junio '22
+523
en 0 canales
Get PRO
mayo '22
+218
en 0 canales
Get PRO
abril '22
+268
en 0 canales
Get PRO
marzo '22
+186
en 0 canales
Get PRO
febrero '22
+424
en 0 canales
Get PRO
enero '22
+734
en 0 canales
Get PRO
diciembre '21
+816
en 0 canales
Get PRO
noviembre '21
+663
en 0 canales
Get PRO
octubre '21
+1 410
en 0 canales
Get PRO
septiembre '21
+565
en 0 canales
Get PRO
agosto '21
+1 525
en 0 canales
Get PRO
julio '21
+1 531
en 0 canales
Get PRO
junio '21
+1 533
en 0 canales
Get PRO
mayo '21
+2 084
en 0 canales
Get PRO
abril '21
+1 811
en 0 canales
Get PRO
marzo '21
+8 672
en 0 canales
Get PRO
febrero '21
+1 828
en 0 canales
Get PRO
enero '21
+1 309
en 0 canales
Get PRO
diciembre '20
+12 495
en 0 canales
Fecha
Crecimiento de Suscriptores
Menciones
Canales
04 julio+1
03 julio+4
02 julio0
01 julio0
Publicaciones del Canal
Получи грант до 3,48 млн на обучение дизайну Поступай на дизайн в Центральный университет с грантом. Для учеников 10–11-х кла
Получи грант до 3,48 млн на обучение дизайну Поступай на дизайн в Центральный университет с грантом. Для учеников 10–11-х классов и СПО. Освой графический, UI/UX и продуктовый дизайн. Создавай визуальные концепты будущего. На программе студенты получают фундаментальную базу, развивают прикладные навыки, приобретают опыт работы над реальными проектами, собирают портфолио и строят связи внутри дизайн-сообщества Подать заявку #реклама 16+ cu.ru О рекламодателе

2
⁣Типизация data-атрибутов и Dataset API — type-safe селекторы без рантайм-багов Data-атрибуты — мощный инструмент для связывания разметки, JS и CSS. Но без типов легко опечататься в data-modal-id или забыть синхронизировать CSS-селектор с JS. В большом проекте это приводит к багам, которые невозможно поймать до тестирования. Как это выглядит в production Опишите контракт через TypeScript template literal types: type DataAttributes = { 'modal-id': 'header' | 'footer'; 'theme': 'light' | 'dark'; 'row-index': number; }; Затем генерируйте type-safe селекторы: type DataSelector<T extends keyof DataAttributes> = [data-${T}=&amp;quot;${DataAttributes[T]}&amp;quot;]; const headerSelector: DataSelector<'modal-id'> = '[data-modal-id=&quot;header&quot;]'; // OK Dataset API с маппингом Создайте функцию, которая приводит к camelCase и проверяет типы: function getDataset<K extends keyof DataAttributes>(el: HTMLElement, attr: K): DataAttributes[K] { const key = attr.replace(/[A-Z]/g, m => -${m.toLowerCase()}); return el.dataset[key] as DataAttributes[K]; } Ошибка: забыть, что data-modal-id в dataset становится modalId. Теперь IDE подсветит невалидные ключи. CSS-переменные из типов Соберите CSS custom properties автоматически: const cssVar = --${headerSelector.replace(/[\[\]=&amp;quot;]/g, '').replace(/\s/g, '-')}; // получите --data-modal-id-header Практические советы - Добавьте eslint-правило, запрещающее querySelector('[data-*]') без типов. Это ловится статическим анализом. - Для больших проектов используйте генерацию схемы из единого источника (например, JSON Schema) и экспорт типов — это защитит от рассинхронизации с дизайном. Вывод: Типизация data-атрибутов через template literal types превращает runtime-ошибки в compile-time, синхронизирует CSS и JS, и снижает когнитивную нагрузку при разработке.
1 007
3
ИИ поможет написать за тебя код, и в то же время помешает найти тебе работу? Ирония судьбы. Мы уже доверили нейросетям генера
ИИ поможет написать за тебя код, и в то же время помешает найти тебе работу? Ирония судьбы. Мы уже доверили нейросетям генерацию компонентов, написание целых функций и даже тестов. Но когда дело доходит до твоего резюме — тот же самый ИИ безжалостно отправляет его в корзину. До 90% откликов отсеиваются алгоритмами ещё до того, как их увидит человек. В 2026 году ты соревнуешься не только с другими разработчиками, но и с машиной, которая оценивает твоё резюме по десяткам критериев за доли секунды. Время играть по новым правилам. 📅 8 июля в 19:00 МСК на бесплатном вебинаре «Как пробить AI-фильтры и получать приглашения на интервью» мы расскажем, как обойти эту систему. На эфире разберем: 🔹 Как AI и ATS отбирают кандидатов прямо сейчас 🔹 Как найти работу в США и Европе 🔹 Что такое «теневой рынок» труда и как использовать его возможности Спикер — Ангелина Волкова, карьерный эксперт AgileFluent. Ангелина знает, как обмануть AI-фильтры: ✔️ Сама придумывает стратегии, которые ведут к офферу ✔️ Обладает большой экспертизой по рынку Северной Америки ✔️ За плечами — более 200 кейсов с международными офферами За время работы AgileFluent уже помогли 800+ специалистам из IT и Digital найти работу в России и за рубежом. Оставляй заявку на вебинар и бронируй место 👉 [ссылка] 👈 Реклама. ООО «Эджайл», ИНН 7810964334, erid: 2Vtzqvs9fcV
1 115
4
⁣CSS в микрофронтендах: почему Layers и Mixins лучше CSS-in-JS Микрофронтенды в монорепе — штука удобная, пока не касаешься CSS. Каждый микросервис тащит свои стили, потом они накладываются друг на друга, кто-то где-то вставляет !important, начинается хаос. Знакомо. Попробовал связку: CSS Modules + CSS Layers + PostCSS Mixins. Дает типизацию, изоляцию и контроль над каскадом. Не идеально, но жить можно. Проблема 1: порядок подключения ломает стили Решается CSS Layers. Явно задаешь приоритет через @layer. Например, слой base (общие компоненты) всегда ниже слоя components (конкретный микрофронт). Делаешь файл layers.css: @layer reset, base, components, utilities; В каждом микрофронте подключаешь: @import '@company/styles/layers.css' layer(components); @layer components { .button { background: var(--color-primary); } } Порядок гарантирован, даже если импорты разбросаны. Типичная ошибка — мешать @import в разные слои в одном файле. Используй одну точку входа для всех слоев. Проблема 2: опечатки в styles.myButton TypeScript тут не поможет — это же CSS. Выход — typed-css-modules. Генерирует .d.ts для всех .module.css. Добавляешь скрипт: "type-check": "typed-css-modules 'packages/*/src/**/*.module.css' --outDir ." Получаешь: // Button.module.css.d.ts export const button: string; IDE подсказывает имена, ошибки ловятся на CI. Practical совет: генерируй типы на pre-commit хуке, чтобы не забывать. Проблема 3: копипаста flex-center, text-truncate по всем модулям Тут помогает postcss-mixins. Заводишь общий файл: /* @company/styles/mixins.css */ @define-mixin flex-center { display: flex; align-items: center; justify-content: center; } В модуле: @import '@company/styles/mixins.css'; .card-header { @mixin flex-center; gap: 0.5rem; } Кода меньше на 30-40%. И стили синхронизированы по всему монорепу. Важно: в продакшене убедись, что postcss-import обрабатывается до postcss-mixins, иначе миксины не раскроются. Архитектура в Nx/Turborepo: - packages/shared/styles — базовые слои + миксины - packages/shared/ui-kit — компоненты с typed-css-modules - apps/mfe-* — каждый микрофронт подключает общие стили, но со своими слоями Ключевые trade-offs: меньше рантайма, чем CSS-in-JS, безопаснее голого CSS. Надежность растет за счет типизации и изоляции каскада. Поддержка масштабируется на сотни компонентов. Ссылки по теме: CSS Layers спецификация typed-css-modules postcss-mixins Вывод: Явное управление каскадом через CSS Layers, типизация через typed-css-modules и переиспользование через PostCSS Mixins превращают CSS Modules из опасного зверя в предсказуемый инструмент для микрофронтендов в монорепе.
1 181
5
Онлайн-магистратура для IT: ИТМО, МИФИ + Яндекс Программы онлайн-магистратуры ИТМО и МИФИ в партнёрстве с Яндексом. Актуальны
Онлайн-магистратура для IT: ИТМО, МИФИ + Яндекс Программы онлайн-магистратуры ИТМО и МИФИ в партнёрстве с Яндексом. Актуальные знания, практическое обучение и гибкий график. Учитесь, совмещая с работой. Доступна господдержка оплаты, отсрочка от армии Перейти на сайт #реклама 16+ practicum.yandex.ru О рекламодателе
1 602
6
Как мы ускорили разработку Frontend в 10х: TSGO, Oxlint, Rsbuild, React Compiler & CodeGen В статье разбираются пять направле
Как мы ускорили разработку Frontend в 10х: TSGO, Oxlint, Rsbuild, React Compiler & CodeGen В статье разбираются пять направлений, в которых получен измеримый эффект. Первое направление — type checking: сравнение TSCheck и TSGO. Второе — linting: сравнение ESLint, Biome и Oxlint. Третье — bundling: переход от Webpack к Vite, затем к Rsbuild. Четвертое — API-контракты: кодогенерация без AI. Пятое — React-оптимизации: использование React Compiler в production. Читать на Habr
1 646
7
Yandex Ecom Open Air — летнее событие про онлайн-продажи Yandex Ecom Open Air 2026 объединяет деловую программу, живое общени
Yandex Ecom Open Air — летнее событие про онлайн-продажи Yandex Ecom Open Air 2026 объединяет деловую программу, живое общение и атмосферу фестиваля в одном потоке. Здесь обсуждают технологии, которые становятся частью среды. Исследуют силы, которые влияют на весь рынок. Находят новые связи, идеи и точки роста. В течение дня пространство фестиваля наполняют выступления, дискуссии, встречи, специальные форматы, музыка и активности партнёров. Присоединиться к происходящему можно из любой точки — на площадке фестиваля или через онлайн-трансляцию. Зарегистрироваться #реклама 18+ ecomfest.ru О рекламодателе
1 605
8
Конец бесплатного PrimeNG, PrimeReact и PrimeVue? Разбираемся, что задумала PrimeTek PrimeTek, бывшая PrimeFaces, запустила P
Конец бесплатного PrimeNG, PrimeReact и PrimeVue? Разбираемся, что задумала PrimeTek PrimeTek, бывшая PrimeFaces, запустила PrimeUI — новую лицензионную модель для экосистемы PrimeNG, PrimeReact и PrimeVue. Это подаётся как унификация бренда, но по сути компания собирает свои ключевые продукты под одной коммерческой оболочкой. Раньше они существовали как набор отдельных библиотек, которые монетизировались через дополнительные продукты и сервисы. Теперь вся экосистема превращается в единый лицензируемый актив. В статье разберёмся, что именно изменилось, что останется бесплатным, в чем сильные стороны новой схемы и какие риски она несёт для команд и компаний. Читать далее
1 676
9
Утраиваем бюджет на продвижение в Директе Запустите первое продвижение в Яндекс Директе с утроенным бюджетом и ИИ-помощником
Утраиваем бюджет на продвижение в Директе Запустите первое продвижение в Яндекс Директе с утроенным бюджетом и ИИ-помощником ✨ Используйте один из промокодов : При пополнении от 10 000 ₽ +20 000 ₽ Промокод START20 При пополнении от 15 000 ₽ +30 000 ₽ Промокод START30 Зарегистрироваться #реклама direct.yandex.ru О рекламодателе
1 688
10
⁣Рефакторинг SSR: renderToString и renderToPipeableStream в React 19 Долгое время renderToString был монолитом: блокировал event loop до полного выплеска HTML, игнорируя Suspense-границы. Результат — пустая страница для пользователя, пока не загрузится всё. React 19 переосмысливает этот подход, добавляя гранулярное кэширование фрагментов и асинхронную потоковую отдачу. Как Suspense ломает статус-кво В React 19 renderToString научился работать с Suspense через механизм компенсированного выброса. Идея: React выдаёт готовый HTML, а то, что висит на Suspense, досылает через renderToPipeableStream. Это означает, что TTFB падает на 40-60%: не нужно ждать все данные сразу, браузер получает каркас страницы, а остальные куски с данными подгружаются постепенно. Гранулярное кэширование: не трогай статику Ключевая фича — кэширование отдельных Suspense-границ. Берёшь «шапку» и «блок комментариев» и кэшируешь их по отдельности. Если в комментариях данные устарели, шапка берётся из кэша без перерендера. Пример: const fragmentCache = new Map(); function renderPage(req, res) { const stream = renderToPipeableStream( <Page userId={req.userId} />, { onShellReady() { res.write(''); stream.pipe(res); }, onFragmentComplete(id, html) { fragmentCache.set(id, html); } } ); } Здесь onFragmentComplete даёт доступ к HTML каждого фрагмента по его id. Можно складывать в Map, Redis или CDN. Статичные блоки (шапка, подвал) перестают рендериться заново, что снижает нагрузку на сервер. Типичная ошибка: игнорировать React Flight Внутри React 19 renderToPipeableStream использует React Flight: режет HTML на чанки, каждый фрагмент — это свой Promise. Когда данные готовы, отправляется чанк. Старый renderToNodeStream выпилили. Ошибка — продолжать использовать renderToString для страниц с динамическими частями (лента, комментарии, графики). Это приводит к блокировке event loop и росту TTFB, особенно при высоком RPS. Практический совет: начинай с анализа Не беги внедрять гранулярное кэширование на каждую страницу. Для простых статичных страниц это оверинжиниринг. Но если у тебя продуктовая страница с частыми обновлениями (например, корзина или рекомендации), профит очевиден. Используй профилировщик React (например, React DevTools Profiler) и измеряй TTFB и TBT до и после. Оптимизируй только узкие места. Вывод: Гранулярное кэширование фрагментов в React 19 через Suspense и renderToPipeableStream — это production-ready паттерн для снижения TTFB и серверной нагрузки на динамических страницах, но требует вдумчивого выбора кандидатов для оптимизации.
1 496
11
📣 Появился новый шанс стать ИИ-специалистом и быстро найти работу МТС и НИУ ВШЭ объявили о старте набора на третий поток магистратуры «Исследования и предпринимательство в искусственном интеллекте». В программу включили 30 оплачиваемых мест от МТС и подготовку в области машинного обучения и ИИ. Для студентов добавили курсы по генеративному искусственному интеллекту, интеллектуальным агентным системам и проектированию ML-систем. Само обучение проходит на реальных кейса, а лучшие студенты смогут пройти стажировку или получить предложение о работе в МТС Web Services прямо во время обучения. Подать заявки на обучение можно по ссылке.
1 602
12
⁣Типизация императивных handlebars-шаблонизаторов в JSX через branded типы и кастомные JSX-фабрики Переход с Handlebars на React часто оставляет за собой строковые шаблоны с {{#each}} и {{#if}}. Прямая передача таких строк в JSX ломает TypeScript, но есть способ это контролировать на уровне типов. Branded типы как контракт данных Branded тип - это строка с уникальным символом, которая не может быть создана без специальной фабрики. Обычная строка не скомпилируется в такой тип, что предотвращает случайную передачу сырого шаблона. type HandlebarsTemplate = string & { __brand: 'Handlebars' }; function hbs(strings: TemplateStringsArray, ...values: any[]): HandlebarsTemplate { return strings.reduce((acc, str, i) => acc + str + (values[i] || ''), '') as HandlebarsTemplate; } function jsx(tag: string, props: { template?: HandlebarsTemplate } | null, ...children: any[]) { if (tag === 'Template' && props?.template) { return compileAndRender(props.template); } return React.createElement(tag, props, ...children); } Типичная ошибка и её устранение Если передать сырую строку напрямую, TypeScript выдаст ошибку: // ❌ Ошибка: Type 'string' is not assignable to type 'HandlebarsTemplate' <Template template="<div>{{user}}</div>" /> Корректный вариант с фабрикой: const template = hbs&lt;div&gt;{{#if user}}Hello {{user.name}}{{/if}}&lt;/div&gt;; function MyComponent() { return <Template template={template} />; } Production-oriented практика Чтобы кастомная JSX-фабрика работала, настрой Babel или TypeScript: добавь @jsx jsx в начале файла или укажи jsxFactory: 'jsx' в tsconfig. Для гибридных проектов это гарантирует, что ни один сырой шаблон не проскочит в рендер без компиляции. Если шаблон использует переменные контекста, добавь generics для типизации payload: function hbs<T>(strings: TemplateStringsArray, ...values: any[]): HandlebarsTemplate<T>; Trade-offs Дополнительный слой абстракции усложняет читаемость и увеличивает bundle, если шаблонов много. Используй такой подход только в пограничных сценариях миграции, а не как постоянную практику. Вывод: Branded типы и кастомные JSX-фабрики позволяют безопасно встраивать императивные шаблоны в декларативный JSX, предотвращая runtime-ошибки на этапе компиляции.
1 563
13
⁣Typed custom hooks: constraints и infer для строгих конфигов Часто вижу, как custom hooks принимают конфиги, где TypeScript выводит mode: string вместо 'edit' | 'view', а permissions — string[] вместо конкретных литералов. В production это ведет к потере автодополнения и багам на стороне потребителей. Constraints: фиксируем рамки Используй extends уже на уровне дженериков хука, чтобы сузить допустимые типы. Без него TS выводит широкие типы, с ним — точные литералы, если исходные данные переданы с as const. function useFeature<M extends string, P extends string[]>(config: { mode: M; permissions: P; }) { // TS: M, P как литералы } Infer: выводим сложные зависимости Когда конфиг содержит вложенные коллбэки или динамические ключи, infer в условных типах автоматически извлекает нужные типы из переданного объекта. Это спасает от ручного аннотирования. type ConfigResult<T> = T extends { mode: infer M; permissions: infer P } ? { mode: M; permissions: P } : never; Типичная ошибка: забытый as const Без as const TS выведет string, а не литералы. А если конфиг приходит из API — этот подход бесполезен. Зато для статически заданных конфигов в хуках (фичи, A/B-тесты, permission guards) это чистый профит: типы не протекают, код самодокументируется. Вывод: Constraints с extends и вывод через infer дают строгую типизацию factory-функций и конфигурационных объектов без потери читаемости и с проверками на уровне компиляции.
1 823
14
Попросили Claude создать WCAG-доступный DataPicker на React и потратили 3 дня на доработки Выбор даты кажется небольшой задач
Попросили Claude создать WCAG-доступный DataPicker на React и потратили 3 дня на доработки Выбор даты кажется небольшой задачей в UI, пока не попробуешь сделать его по-настоящему WCAG-доступным. Нам понадобился настраиваемый DataPicker на React для процесса записи на прием к врачу, где пользователи, работающие с keyboard navigation, и люди, использующие screen reader’ы, должны были выбрать дату без лишних затруднений. Claude сделал нам хорошую первую версию: структуру компонента, ARIA-атрибуты, базовую keyboard navigation и логику календаря. На первый взгляд результат выглядел почти готовым. Затем мы запустили NVDA, VoiceOver и протестировали сценарий keyboard navigation. Фокус выходил за пределы диалогового окна; некоторые даты озвучивались неверно; переключение между месяцами сопровождалось слишком тихим звуком; нажатие клавиши «Esc» закрывало календарь, но оставляло пользователя без контекста; режим высокой контрастности Windows нарушал отображение выбранного состояния. Код выглядел нормально, но UX оставлял желать лучшего. В этой статье мы рассмотрим реальную работу, стоящую за WCAG-доступным DataPicker'ом: где AI сэкономил нам время; где он не справился; как нам помог WAI-ARIA APG ; какие детали нам пришлось исправлять вручную и почему доступность нельзя проверить, просто прочитав сгенерированный код. Читать далее
1
15
Два способа создания доступного DatePicker'а с помощью AI: 80/20 или системное проектирование DatePicker потребовался на Reac
Два способа создания доступного DatePicker'а с помощью AI: 80/20 или системное проектирование DatePicker потребовался на React и TypeScript с корректной работой keyboard navigation, screen reader'а, управляемым состоянием и проверками доступности. Первый способ — дать AI четкий запрос, получить 80% кода, остальное доработать руками. Модель генерирует структуру календаря, атрибуты ARIA, базовую keyboard navigation и логику работы с датами. Затем начинаются проблемы: поведение фокуса становится нестабильным, возникают конфликты обработчиков событий, озвучивание screen reader'ами требует тестирования, небольшое изменение в логике может нарушить календарь. Второй способ — системное проектирование с AI-агентом. Включает PRD, декомпозицию задач, правила агента, внешнюю верификацию, Vitest, Playwright, сборку Vite, проверки типов и строгий цикл: агент не может двигаться дальше, пока не пройден текущий шаг. Далее разбирается, в чем AI действительно помог, где начал сбиваться, почему одного большого запроса недостаточно, как The Verifier изменил процесс и почему задача инженера сводится к контролю над замыслом, архитектурой, контрактами и стоимостью изменений. Читать далее на Habr
1 978
16
⁣React 19: ErrorBoundary с типизацией, error.cause и строгое логирование Границы ошибок в React долго выглядели как костыль для асинхронщины: раньше ErrorBoundary отлавливал только синхронные ошибки, а запросы с then/catch просто пролетали мимо. В 19 версии это починили через use и новые хуки, но типизация все равно остается местом, где можно наступить на грабли в production. Интерфейс и цепочка ошибок Интерфейс ErrorBoundaryState с Error | null, getDerivedStateFromError ловит ошибку, componentDidCatch отправляет в лог. Но что действительно спасает в реальных кейсах — это error.cause из ES2022. Когда fetch падает с 401, ты пробрасываешь не строку, а объект: * error.cause?.status для проверки статуса * error.cause?.message для текста interface AppError extends Error { cause?: { status: number; message: string; }; } Production: сужение и безопасность Передавать полный stack клиенту — плохая идея. Там пути файлов и внутренние адреса. Мы обрезаем первые 200 символов, если ошибка некастомная, и никогда не светим причину наружу: error.cause только во внутренний логгер. Схема логирования через instanceof: * если error instanceof AppError — пишем структурированно с типом, причиной и стеком * если просто Error — обрезаем стек, кидаем предупреждение * если вообще не Error — логируем как строку и показываем generic fallback Типичная ошибка instanceof не работает, если ошибка прилетела из другого iframe или realm. Там error.cause может быть сериализованным объектом, и придется проверять по полям вручную. Встречал такое при интеграции с микросервисными виджетами. Еще один момент — не засовывай всю логику в componentDidCatch: вынеси ее в отдельную функцию handleFatalError с возвратом never и переиспользуй, иначе при рефакторинге придется переписывать каждый Boundary. Вывод: Типизированный ErrorBoundary с error.cause и строгим логированием через instanceof — это надежный паттерн для обработки асинхронных ошибок, который дает консистентность и безопасность в production. Источники: React docs про Error Boundaries, MDN про Error.cause, React 19 release notes про use() hook и async errors.
2 085
17
⁣Типизация ErrorBoundary в React 19: асинхронные ошибки, error.cause и production-логирование ErrorBoundary долго был слабым звеном — он не ловил ошибки из промисов, useEffect или setTimeout. React 19 не меняет классовый подход, но появление error.cause и правильная типизация через instanceof делают обработку асинхронных ошибок предсказуемой и безопасной. Почему any в state — ошибка Состояние границы должно быть строго типизировано: Error | null. В getDerivedStateFromError проверяй cause через instanceof Error, не через truthy check. Это исключает мусор из продакшена, где cause может быть строкой, числом или undefined. Production-кейс с fetch При HTTP 500 выбрасывай: throw new Error('HTTP error', { cause: res.status }) В границе проверяй: if (error.cause instanceof Error) { // Логируем полный стек } else if (typeof error.cause === 'number') { // Игнорируем, если статус < 500 } Это позволяет не слать в Sentry временные сетевые сбои. Типизация cause и instanceof Используй instanceof для разграничения классов ошибок — HTTPError, NetworkError, ValidationError. Так ты контролируешь, что идёт в Sentry, а что — только в консоль. Без этого логгер захлебнётся шумом. Вывод: Типизированный ErrorBoundary с error.cause и instanceof превращает асинхронные ошибки из невидимых багов в управляемые инциденты.
2 135
18
⁣Как вывести типы контекстов во вложенных layout без ручных интерфейсов в React Router v7 Если вы используете TanStack Router (React Router v7) с вложенными layout, то наверняка сталкивались с ситуацией, когда родительский layout прокидывает loader-контекст, а дочерний компонент вынужден вручную объявлять интерфейс и кастить. Типовая ошибка — забыть обновить интерфейс при изменении структуры данных, что ведет к багам на проде. Как работает типовая цепочка Система типов строится вокруг дженериков createRoute. Каждый роут наследует контекст от родителя через getParentRoute(). TypeScript автоматически мержит результаты loader родительского и текущего роута: const rootRoute = createRootRoute()({ loader: () => ({ user: 'John' }) }); const layoutRoute = createRoute({ getParentRoute: () => rootRoute, loader: ({ context }) => ({ theme: context.user === 'John' ? 'dark' : 'light' }) }); const pageRoute = createRoute({ getParentRoute: () => layoutRoute, loader: ({ context }) => { // context: { user: string, theme: string } return context.theme; } }); Ни одного явного интерфейса или каста. Система выводит тип контекста из цепочки родительских роутов. Production-кейс: аутентификация с правами Практический пример: вложенный layout с авторизацией. Родитель кладет user, дочерний — permissions: const authLayout = createRoute({ getParentRoute: () => rootRoute, loader: () => ({ user: fetchUser() }) }); const adminLayout = createRoute({ getParentRoute: () => authLayout, loader: ({ context }) => ({ permissions: fetchPermissions(context.user.id) }) }); Страница получает { user, permissions } без ручного объявления. Если переименовать user в currentUser — TypeScript подсветит все места, где используется старый ключ. Типичная ошибка и trade-off Ошибка: ручное объявление interface IContext = { user: string } и последующий as any при несоответствии. Это ломает всю type-safety и ведет к runtime-ошибкам при изменении данных. Trade-off: полагаться на вывод типов чуть сложнее читать в IDE, чем явные интерфейсы, но выигрыш в надежности при рефакторинге — код сам документирует контракты, и компилятор ловит несоответствия. Вывод: Используйте автоматический вывод типов контекстов через цепочку getParentRoute, чтобы избавиться от ручных интерфейсов и кастингов, повысив надежность и упростив рефакторинг в production-коде.
1 979
19
⁣Signal vs Observable: почему ваш граф зависимостей взрывается в production Когда в корзине интернет-магазина total = price * qty, затем скидка от суммы, затем налог — Observable (RxJS) на каждое изменение price пересчитывает все подписки, даже если данные не изменились. Разработчики навешивают distinctUntilChanged на каждую цепочку, и через месяц граф превращается в лапшу. Как работают Signal Signal (SolidJS, Vue 3.4+, Preact Signals, Svelte 5) делает вычисления ленивыми. Он отслеживает, какие сигналы реально читаются в computed, а не подписывается на всё подряд. Два сигнала, зависящих от одного источника, не подписываются дважды. При изменении источника пересчитываются только те, кто на него ссылается, а не всё дерево. Пример на псевдокоде Solid: count = signal(1) price = signal(100) total = computed(() => count() * price()) discount = computed(() => total() > 500 ? 0.1 : 0) final = computed(() => total() * (1 - discount())) При изменении count пересчитываются total и final. Discount пересчитывается только если total пересечет порог. В RxJS через combineLatest discount будет пересчитываться на каждое изменение count и price, даже если total не изменился. Меньше кода — больше бесполезной работы. Когда Observable все еще нужен Observable (RxJS) хорош для асинхронных цепочек: debounce, switchMap, WebSocket. Это декларативные потоки, где важны трансформации во времени. Но при сотне зависимостей начинается цирк: каждый чих триггерит пересчёт, дебажить dependency hell — отдельный квест. Типичная ошибка — использовать Observable для синхронного графа зависимостей, где сигналы дают ровно то же самое без лишних пересчётов. Практический совет Если у вас в проекте уже RxJS — не переписывайте всё. Для нового фича с interdependent состояниями (формы, редакторы, дашборды) закладывайтесь на сигналы. В продакшене с сотнями зависимостей сигналы дают на 30-50% меньше бесполезных перерисовок и упрощают отладку: граф зависимостей прозрачен, а не взрывается на каждой итерации. Вывод: Signal — для синхронных графов с предсказуемыми изменениями, Observable — для event-driven асинхронных потоков, и выбор между ними — это trade-off между производительностью вычислений и гибкостью трансформаций.
2 049
20
🤣 Общаться о синтаксисе теперь тоже можно через агентов ✖️ xCode Journal
🤣 Общаться о синтаксисе теперь тоже можно через агентов ✖️ xCode Journal
2 068