Альтернативные инструменты к посту выше, которые не только типы, но и хэндлеры запросов сгенерят по API схеме
https://heyapi.dev - пока в бете, активно разрабатывается и выглядит многообещающе. Финальная генерация кода лаконичная. В целом, понятная документация и конфигурация 👍
https://orval.dev &
https://kubb.deve чем-то похожие инструменты - последний генерирует более чистый и не многословный код. Но много вещей скрыто под капотом. Документация на мой взгляд не интуитивно понятная и конфигурация требует экспериментов, чтобы получить желаемое 🤔
Я пока предпочитаю https://openapi-ts.dev/, да приходится писать хэндлеры в ручную, но зато просто, гибко и понятно в конфигурации ✅
#FrontendBeer #typescript #openapi #codegen #tanstackquery #fetch #axios (c) Frontend Beer
https://heyapi.dev - пока в бете, активно разрабатывается и выглядит многообещающе. Финальная генерация кода лаконичная. В целом, понятная документация и конфигурация 👍
https://orval.dev &
https://kubb.deve чем-то похожие инструменты - последний генерирует более чистый и не многословный код. Но много вещей скрыто под капотом. Документация на мой взгляд не интуитивно понятная и конфигурация требует экспериментов, чтобы получить желаемое 🤔
Я пока предпочитаю https://openapi-ts.dev/, да приходится писать хэндлеры в ручную, но зато просто, гибко и понятно в конфигурации ✅
#FrontendBeer #typescript #openapi #codegen #tanstackquery #fetch #axios (c) Frontend Beer
❤1👍1
TypeScript переписан
на Go! Обещают 10 кратное увеличение производительности 🚀 Читай подробнее в официальном блоге 🔗
Осталось только дождаться 7 версии
#FrontendBeer #typescript (c) Frontend Beer
на Go! Обещают 10 кратное увеличение производительности 🚀 Читай подробнее в официальном блоге 🔗
Осталось только дождаться 7 версии
#FrontendBeer #typescript (c) Frontend Beer
Microsoft News
A 10x Faster TypeScript
Embarking on a native port of the existing TypeScript compiler and toolset to achieve a 10x performance speed-up.
😱3⚡2
В параллель с этим развивается нативная поддержка TS в Node.js
На мой взгляд это хороший двигатель прогресса, на фоне Deno
Интересно понаблюдать, как когда-то за гонкой менеджеров пакетов
#FrontendBeer #typescript #deno (c) Frontend Beer
На мой взгляд это хороший двигатель прогресса, на фоне Deno
Интересно понаблюдать, как когда-то за гонкой менеджеров пакетов
#FrontendBeer #typescript #deno (c) Frontend Beer
nodejs.org
Node.js — Node.js 22.6.0 (Current)
Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.
Кстати, попробовать новую версию TS можно используя этот репозиторий
#FrontendBeer #typescript (c) Frontend Beer
#FrontendBeer #typescript (c) Frontend Beer
GitHub
GitHub - microsoft/typescript-go: Staging repo for development of native port of TypeScript
Staging repo for development of native port of TypeScript - microsoft/typescript-go
EOL / EOS: что это и почему важно?
Каждая библиотека или программа, проходит через жизненный цикл: разработка новой версии, активная поддержка, затем постепенное завершение разработки и поддержки выпущенной версии. И так по кругу 🔄
📌 EOL (End of Life) — момент, когда продукт перестает получать какие-либо обновления от разработчиков
📌 EOS (End of Support) — момент, когда прекращается не только разработка, но и техническая поддержка (включая исправление уязвимостей)
Что это значит? Использование таких технологий становится небезопасным, так как в них могут обнаруживаться новые уязвимости, но исправлять их уже никто не будет
В недавней email рассылке касательно Node.js, натолкнулся на интересный инструмент endoflife.date.
Он позволяет следить за жизненным циклом используемых инструментов в разработке и поддержке ПО.
Можно просто иногда обращаться к текстовой версии, на самом сайте. А если хотите сделать свой дэшборд - можно воспользоваться API.
Нет нужного инструмента в списке? Всегда можно законтрибьютить 📝
#FrontendBeer #eol #eos #endoflife #date (c) Frontend Beer
Каждая библиотека или программа, проходит через жизненный цикл: разработка новой версии, активная поддержка, затем постепенное завершение разработки и поддержки выпущенной версии. И так по кругу 🔄
📌 EOL (End of Life) — момент, когда продукт перестает получать какие-либо обновления от разработчиков
📌 EOS (End of Support) — момент, когда прекращается не только разработка, но и техническая поддержка (включая исправление уязвимостей)
Что это значит? Использование таких технологий становится небезопасным, так как в них могут обнаруживаться новые уязвимости, но исправлять их уже никто не будет
В недавней email рассылке касательно Node.js, натолкнулся на интересный инструмент endoflife.date.
Он позволяет следить за жизненным циклом используемых инструментов в разработке и поддержке ПО.
Можно просто иногда обращаться к текстовой версии, на самом сайте. А если хотите сделать свой дэшборд - можно воспользоваться API.
Нет нужного инструмента в списке? Всегда можно законтрибьютить 📝
#FrontendBeer #eol #eos #endoflife #date (c) Frontend Beer
endoflife.date
Home
Check end-of-life, support schedule, and release timelines for more than 380+ products at one place.
👍1🔥1
💡 Styled-components официально переходит в режим поддержки
Evan Jacobs объявил о завершении активной разработки styled-components. В своем заявлении он поблагодарил сообщество за вклад и поддержку, отметив, что многие идеи библиотеки стали стандартом в экосистеме CSS-in-JS.
Если вы всё ещё используете styled-components, сейчас самое время подумать о миграции на другие альтернативы: CSS modules / Tailwind / и пр.
#FrontendBeer #styledcomponents #css-in-js (c) Frontend Beer
Evan Jacobs объявил о завершении активной разработки styled-components. В своем заявлении он поблагодарил сообщество за вклад и поддержку, отметив, что многие идеи библиотеки стали стандартом в экосистеме CSS-in-JS.
Если вы всё ещё используете styled-components, сейчас самое время подумать о миграции на другие альтернативы: CSS modules / Tailwind / и пр.
#FrontendBeer #styledcomponents #css-in-js (c) Frontend Beer
Opencollective
Thank you - styled-components
First and foremost, thank you to everyone who has contributed to styled-components over the years. Open Source is hard work, and many of the larger feature and/or refactoring drives probably would never have shipped without your support! As...
👌2🔥1😢1
Хотел написать сегодня пост про Mutation Observer и как его можно применять, на примере компонента табов… но пока готовил пост и репу с кодом понял, что сегодня не успею 😀
Поэтому вот небольшая история из того, что словил в процессе подготовки
Взял я значит шаблон для сборки проекта на Vite с уже подключенным React & Mantine. Далее подготовил код и пушнул в репу вместе со всеми коммитами автора шаблона 🥶
И вот собственно небольшой гайд как сделать так, чтобы оставить только свой последний коммит (для только что созданной репы), если уже все запушено
Вот и все 🙂
А про Mutation Observer уже завтра расскажу
#FrontendBeer #git #articlenotes #заметки (c) Frontend Beer
Поэтому вот небольшая история из того, что словил в процессе подготовки
Взял я значит шаблон для сборки проекта на Vite с уже подключенным React & Mantine. Далее подготовил код и пушнул в репу вместе со всеми коммитами автора шаблона 🥶
И вот собственно небольшой гайд как сделать так, чтобы оставить только свой последний коммит (для только что созданной репы), если уже все запушено
git checkout --orphan new-branch # Cоздаем новую ветку без истории изменений
git add -A
git commit -m "TEXT OF YOUR LAST COMMIT"
git branch -D main # Удаляем старую ветку
git branch -m main # Переименовываем нашу новую ветку new-branch обратно в main
git push --force origin main # ну и затираем старые изменения
Вот и все 🙂
А про Mutation Observer уже завтра расскажу
#FrontendBeer #git #articlenotes #заметки (c) Frontend Beer
Telegram
Frontend Beer
Обсудим фронтенд под кружку крафтового
EN version: https://t.iss.one/frontendbeer_en
Автор / Author: @dipiash
EN version: https://t.iss.one/frontendbeer_en
Автор / Author: @dipiash
👍3🔥1👏1😁1🎉1
В статье я разобрал:
- Как сделать скроллящийся контейнер с вкладками
- Как подскролливать к активной вкладке
- Как адаптировать компонент под изменения размеров окна
Mutation Observer, scrollIntoView, requestAnimationFrame и вот это вот все
Cтатья тут 👉 Tabs component with scrolling support со ссылкой на пример с кодом
Demo 📜: https://dipiash.github.io/mantine-scrollable-tabs/
#FrontendBeer #mutationobserver #react #typescript #mantine #tabs (c) Frontend Beer
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2❤1👍1
🚀 Dokploy. Удобный деплой без лишних движений
Задумал очередной пет-проект (парсинг слотов на визу), навайбкодил, всё отладил. Пора деплоить на сервер
Классика: настроить сервер, закинуть SSH ключи, накатить Docker, прикрутить CI, прописать env и пр. Делал это уже миллион раз через консоль. Но в этот раз захотелось чуть проще. Как на Vercel, только без serverless-ограничений
Решил попробовать Dokploy
Интерфейс простой:
1. ставим через bash-скрипт
2. регаем пользователя
3. подключаем GitHub репу
4. указываем путь к docker-compose для проекта
5. добавляем пару env переменных
И всё. Проект деплоится. На каждый пуш - автообновление. Никакой CI писать не нужно
Когда понимаешь, как всё работает под капотом, особенно приятно найти инструмент, который делает часть вещей за тебя
#docker #deployment #devops #github #dokploy #FrontendBeer
Задумал очередной пет-проект (парсинг слотов на визу), навайбкодил, всё отладил. Пора деплоить на сервер
Классика: настроить сервер, закинуть SSH ключи, накатить Docker, прикрутить CI, прописать env и пр. Делал это уже миллион раз через консоль. Но в этот раз захотелось чуть проще. Как на Vercel, только без serverless-ограничений
Решил попробовать Dokploy
Интерфейс простой:
1. ставим через bash-скрипт
2. регаем пользователя
3. подключаем GitHub репу
4. указываем путь к docker-compose для проекта
5. добавляем пару env переменных
И всё. Проект деплоится. На каждый пуш - автообновление. Никакой CI писать не нужно
Когда понимаешь, как всё работает под капотом, особенно приятно найти инструмент, который делает часть вещей за тебя
#docker #deployment #devops #github #dokploy #FrontendBeer
Dokploy
Dokploy - Deploy your applications with ease
Deploy your applications with ease using Dokploy
👍5🔥3
Есть у меня репозиторий с boilerplate проектом в котором я давно не обновлял зависимости. Что же пришла пора
Обновив ключевые зависимости пушнул ветку, завел PR на который закрутились Github Action... и action завис на этапе установки
node_modules через npm ciДальше этих строчек лог установки не идет
...
npm warn deprecated [email protected]: This SVGO version is no longer supported. Upgrade to v2.x.x.
npm warn deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm warn deprecated [email protected]: This version is no longer supported. Please see https://eslint.org/version-support for other options.
Что можно сделать с этим?
1. Для начала включить полный лог, через параметр
--verbose:
npm ci --prefer-offline --verbose
Что же видим лог, что все модули скачиваются => сетевых проблем нет, но лог пополнился информацию о том, что выполняются postinstall скрипты в некоторых модулях. Опять дальше этих строк ничего не идет:
...
npm info run @swc/[email protected] postinstall node_modules/@swc/core node postinstall.js
npm info run [email protected] postinstall node_modules/core-js node -e "try{require('./postinstall')}catch(e){}"
npm info run [email protected] postinstall node_modules/core-js-pure node -e "try{require('./postinstall')}catch(e){}"
npm info run [email protected] postinstall { code: 0, signal: null }
npm info run [email protected] postinstall node_modules/esbuild node install.js
npm info run [email protected] postinstall { code: 0, signal: null }
npm info run [email protected] postinstall node_modules/nx node ./bin/post-install
npm info run [email protected] postinstall { code: 0, signal: null }
npm info run [email protected] postinstall node_modules/unrs-resolver napi-postinstall unrs-resolver 1.10.1 check
npm info run [email protected] postinstall { code: 0, signal: null }
npm info run [email protected] postinstall node_modules/@modern-js/node-bundle-require/node_modules/esbuild node install.js
npm info run [email protected] postinstall { code: 0, signal: null }
npm info run @swc/[email protected] postinstall { code: 0, signal: null }
2. Включаем лог для postinstall скриптов
С npm v7+, скрипты postinstall, preinstall, и другие lifecycle-скрипты выполняются в фоне. И чтобы увидеть логи, нужно добавить опцию
--foreground-scripts:
npm ci --prefer-offline --verbose --foreground-scripts
Здорово, теперь в логах можно увидеть:
npm info run @swc/[email protected] postinstall node_modules/@swc/core node postinstall.js
> @swc/[email protected] postinstall
> node postinstall.js
Error: Failed to load native binding
...
Error: Cannot find module './swc.linux-x64-gnu.node'
...
@swc/core was not able to resolve native bindings installation. It'll try to use @swc/wasm as fallback instead.
npm info run @swc/[email protected] postinstall { code: 0, signal: null }
npm info run [email protected] postinstall node_modules/core-js node -e "try{require('./postinstall')}catch(e){}"
> [email protected] postinstall
> node -e "try{require('./postinstall')}catch(e){}"
npm info run [email protected] postinstall { code: 0, signal: null }
npm info run [email protected] postinstall node_modules/core-js-pure node -e "try{require('./postinstall')}catch(e){}"
> [email protected] postinstall
> node -e "try{require('./postinstall')}catch(e){}"
npm info run [email protected] postinstall { code: 0, signal: null }
npm info run [email protected] postinstall node_modules/esbuild node install.js
> [email protected] postinstall
> node install.js
npm info run [email protected] postinstall { code: 0, signal: null }
npm info run [email protected] postinstall node_modules/nx node ./bin/post-install
> [email protected] postinstall
Тут видно, что не хватает нативных платформенных зависимостей - именно для CI. Это решается довольно просто, докинем в установку:
npm add @swc/core-linux-x64-gnu @esbuild/linux-x64 @rollup/rollup-linux-x64-gnu
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3❤1👍1
Окей, CIка крутится - ошибки ушли для всех postinstall скриптов, но все равно установка не происходит до конца
3. Какие варианты? Только смотреть на зависимости и их changelog, чтобы понять где проблема
Ну и строки в логах c NX не дают покая:
Пробую отключить postinstall скрипты через флаг
Можно было полезть в сорсы и начать дебажить, а что там происходит, но мне хотелось сначала найти рабочее решение. И тут можно воспользоваться подходом похожим на git-bisect и просто найти рабочую версию NX, максимально близку к последней. Что я собственно и сделал
В последней итерации попробовал обновить NX до бета версии:
#frontops #npm #actions #github_actions #nx #FrontendBeer
3. Какие варианты? Только смотреть на зависимости и их changelog, чтобы понять где проблема
Ну и строки в логах c NX не дают покая:
npm info run [email protected] postinstall node_modules/nx node ./bin/post-install
> [email protected] postinstall
Пробую отключить postinstall скрипты через флаг
--ignore-scripts - но это снова не помогает, хоть установка и проходит, но перестали работать команды NX - тоже сатил зависать. Все-таки чего-то не хватает. Можно было полезть в сорсы и начать дебажить, а что там происходит, но мне хотелось сначала найти рабочее решение. И тут можно воспользоваться подходом похожим на git-bisect и просто найти рабочую версию NX, максимально близку к последней. Что я собственно и сделал
В последней итерации попробовал обновить NX до бета версии:
21.3.0-beta.0 и там тоже Github Actions завелись ⭐️️ Теперь можно в спокойной обстановке покопаться в сорцах NX и понять, чтоже там поменяли#frontops #npm #actions #github_actions #nx #FrontendBeer
👍3🔥2
Production Ready слушатель изменений в Postgres через Supabase Realtime
Сегодя будет не про фронтенд, да и ладно👌 подумываю о переименовании канала 🤔
Давно присматривался к Supabase - Postgres, миграции, генерация типов, хороший тулинг. Но документация слабая - только базовые примеры, а все интересное только черзболь и страдания опыт. Решил попробовать Realtime подписку на изменения в таблицах (этакий Firebase)
Собрал два сервиса:
- краулер ходит по сайтам и складывает данные в БД
- Telegram-бот шлёт уведомления в канал, если данные подходят под заданные условия
И захотелось мне, чтобы Telegram-бот слушал изменения в таблице, в которую складывает данные краулер
Ожидание: завелось просто и быстро
Реальность: соединение висит ~ раз в 30 минут падает. Реконнектов нет, в доке/интерфейсах - тишина. Гитхаб-ишьюсы (раз, два) тоже не спасли, но натолкнули на мысли. Ок, идём разбираться.
В итоге потратил пару дней, чтобы отловить все кейсы падений и получить рабочее решение
✅ Обработал статусы соедения у канала (успех/ошибка/закрыт) и завёл ретраи на неуспешные
✅ Решил проблему с удалением существующего канала, но все равно пришлось перейти на уникальное имя канала с использование
✅ Добавил экспоненциальный бэкофф с лимитом попыток, чтобы не спамить реконнектами
✅ Сложил всё в плюс-минус универсальный интерфейс, который можно расширять и переиспользовать
Было интересно разобраться в новом инструменте => теперь подписки на изменения держатся и предсказуемо восстанавливается 🙂
Итоги изысканий преложил в статью.
Прочитать и найти код можно здесь 👉: Production-ready listener for Supabase realtime Postgres changes in Node.js
#nodejs #supabase #postgres #typescript #FrontendBeer
Сегодя будет не про фронтенд, да и ладно
Давно присматривался к Supabase - Postgres, миграции, генерация типов, хороший тулинг. Но документация слабая - только базовые примеры, а все интересное только черз
Собрал два сервиса:
- краулер ходит по сайтам и складывает данные в БД
- Telegram-бот шлёт уведомления в канал, если данные подходят под заданные условия
И захотелось мне, чтобы Telegram-бот слушал изменения в таблице, в которую складывает данные краулер
Ожидание: завелось просто и быстро
Реальность: соединение висит ~ раз в 30 минут падает. Реконнектов нет, в доке/интерфейсах - тишина. Гитхаб-ишьюсы (раз, два) тоже не спасли, но натолкнули на мысли. Ок, идём разбираться.
В итоге потратил пару дней, чтобы отловить все кейсы падений и получить рабочее решение
✅ Обработал статусы соедения у канала (успех/ошибка/закрыт) и завёл ретраи на неуспешные
✅ Решил проблему с удалением существующего канала, но все равно пришлось перейти на уникальное имя канала с использование
Date.now, во избежании коллизий - потому что очистка не помогала и подписка все равно валилась и восстанавливалась на второй раз. Для моего проекта пойдет, в идеале доабвлять uuid постфикс✅ Добавил экспоненциальный бэкофф с лимитом попыток, чтобы не спамить реконнектами
✅ Сложил всё в плюс-минус универсальный интерфейс, который можно расширять и переиспользовать
Было интересно разобраться в новом инструменте => теперь подписки на изменения держатся и предсказуемо восстанавливается 🙂
Итоги изысканий преложил в статью.
Прочитать и найти код можно здесь 👉: Production-ready listener for Supabase realtime Postgres changes in Node.js
#nodejs #supabase #postgres #typescript #FrontendBeer
Please open Telegram to view this post
VIEW IN TELEGRAM
⚡2🔥2👏1
Сегодня разберу как подружить TanStack DB и TanStack Query
Недавно у меня была задача уменьшить количество релоадов данных и показа лоадеров в таблицах при открытии/закрытии различных карточек с данными поверх таблиц. Данные в карточке могут быть изменены и затем карточка закрывается. После этого таблица перезапрашивала данные и показывала лоадер, что бы после показать в колонках актуальные данные
Надо отметить, что все приложение построено на React Query + несколько контекстов для глобальных данных. То есть глобального стейт менеджера типа zustand или redux - нет. И в целом этого хватало
Но для таблиц поведение оказалось не очень удобным. И чтобы решить задачу по повышению концентрации пользователя при работе с данными в таблице и предотвратить потерю фокуса я решил, что пора заводить стейт менеджер. Тем более что предстоят задачи в которых без него будет не просто обойтись. Конечно, если нет времени и желания написать весь инфраструктурный код самостоятельно 😃 Keep it simple, как говорится
И выбор мой пал на TanStack DB. Экосистема у TanStack довольно хорошая и уже есть глубокая интеграция с TanStack Query и TanStack Table в проект. Начал разбираться как подружить между собой TanStack Query и TanStack DB, чтобы грузить часть коллекции в стор, а из карточки обновлять затронутые ячейки таблицы через инструменты стора
#tanstack #react #tanstackdb #tanstackquery #FrontendBeer
Недавно у меня была задача уменьшить количество релоадов данных и показа лоадеров в таблицах при открытии/закрытии различных карточек с данными поверх таблиц. Данные в карточке могут быть изменены и затем карточка закрывается. После этого таблица перезапрашивала данные и показывала лоадер, что бы после показать в колонках актуальные данные
Надо отметить, что все приложение построено на React Query + несколько контекстов для глобальных данных. То есть глобального стейт менеджера типа zustand или redux - нет. И в целом этого хватало
Но для таблиц поведение оказалось не очень удобным. И чтобы решить задачу по повышению концентрации пользователя при работе с данными в таблице и предотвратить потерю фокуса я решил, что пора заводить стейт менеджер. Тем более что предстоят задачи в которых без него будет не просто обойтись. Конечно, если нет времени и желания написать весь инфраструктурный код самостоятельно 😃 Keep it simple, как говорится
И выбор мой пал на TanStack DB. Экосистема у TanStack довольно хорошая и уже есть глубокая интеграция с TanStack Query и TanStack Table в проект. Начал разбираться как подружить между собой TanStack Query и TanStack DB, чтобы грузить часть коллекции в стор, а из карточки обновлять затронутые ячейки таблицы через инструменты стора
#tanstack #react #tanstackdb #tanstackquery #FrontendBeer
❤2👍2
В документации TanStack DB везде приводятся примеры, где
На самом деле TanStack DB - это лишь хранилище, а то, как вы получаете данные и наполянете хранилище, полностью зависит от вас. В текущей версии TanStack DB ещё не поддерживает параметризованные запросы и может выполнять live‑query только по всей коллекции
Поэтому я решил задачу следующим образом: комбинировал TanStack Query и TanStack DB. TanStack Query отвечает за сетевые запросы и параметры (страница, количество элементов, сортировка и фильтры), а TanStack DB служит только для хранения, изменения и выдачи записей
Для каждого нового набора параметров я вызываю
Коллекция синхронизируется не через запрос на сервер в
Такой подход позволяет хранить данные по ID, выполнять оптимистичные обновления и не нужно использовать фабрику для создания коллекций под каждый фильтр. Мне кажется оверхедом иметь коллекцию под каждый набор параметров, когда фильтров на странице может переваливать за несколько десятков. А также не всегда в приложении есть потребность держать все данные со всех запросов сразу в памяти, поэтому отдельные коллекции под каждый запрос - это может быть не оптимально. Перезаписываемой коллекции, которую вы очищаете и заново наполняете при смене параметров, может оказаться более чем достаточно
Таким образом, DB хранит только текущий набор данных, а React Query обеспечивает параметризированную загрузку. И использование инструмента избавляет от необходимости вручную патчить кэш useQuery и писать всю обвязку для нормализации / получения данных по id из стора / обновление стора / и пр.
TanStack развивает еще один классный инструмент, который отлично встраивается в экосистему предоставляемых инструментов
#tanstack #react #tanstackdb #tanstackquery #FrontendBeer
queryFn грузит целую коллекцию, поэтому создаётся впечатление, что TanStack DB рассчитан только на сценарии «загрузи всё сразу»На самом деле TanStack DB - это лишь хранилище, а то, как вы получаете данные и наполянете хранилище, полностью зависит от вас. В текущей версии TanStack DB ещё не поддерживает параметризованные запросы и может выполнять live‑query только по всей коллекции
Поэтому я решил задачу следующим образом: комбинировал TanStack Query и TanStack DB. TanStack Query отвечает за сетевые запросы и параметры (страница, количество элементов, сортировка и фильтры), а TanStack DB служит только для хранения, изменения и выдачи записей
Для каждого нового набора параметров я вызываю
useQuery, получаю от сервера нужную порцию данных и в рамках одной коллекции очищаю хранилище и вставляю новые элементы = «wipe & insert», используя writeBatch для удаления старых ключей и вставки новых данныхКоллекция синхронизируется не через запрос на сервер в
queryFn, а напрямую из кеша TanStack Query (`queryClient.getQueryData`), поэтому она ничего не запрашивает по сетиТакой подход позволяет хранить данные по ID, выполнять оптимистичные обновления и не нужно использовать фабрику для создания коллекций под каждый фильтр. Мне кажется оверхедом иметь коллекцию под каждый набор параметров, когда фильтров на странице может переваливать за несколько десятков. А также не всегда в приложении есть потребность держать все данные со всех запросов сразу в памяти, поэтому отдельные коллекции под каждый запрос - это может быть не оптимально. Перезаписываемой коллекции, которую вы очищаете и заново наполняете при смене параметров, может оказаться более чем достаточно
Таким образом, DB хранит только текущий набор данных, а React Query обеспечивает параметризированную загрузку. И использование инструмента избавляет от необходимости вручную патчить кэш useQuery и писать всю обвязку для нормализации / получения данных по id из стора / обновление стора / и пр.
TanStack развивает еще один классный инструмент, который отлично встраивается в экосистему предоставляемых инструментов
#tanstack #react #tanstackdb #tanstackquery #FrontendBeer
GitHub
Using Tanstack DB without fetching the entire collection · TanStack db · Discussion #469
Hi! I really like the look of TanStack DB, but I'm pretty puzzled as to how you work with a collection where it's not feasible to fetch the entire collection at once. In working with an app...
❤2👍2
Пошаговый гайд с примерами кода, оптимистичными обновлениями и типовыми ошибками можно найти в моей статье. Надеюсь, этот паттерн будет полезен!
Прочитать и найти код можно здесь 👉: TanStack DB + TanStack Query: Step-by-step guide to combining parameter-based loading and normalized storage
#tanstack #react #tanstackdb #tanstackquery #FrontendBeer
Прочитать и найти код можно здесь 👉: TanStack DB + TanStack Query: Step-by-step guide to combining parameter-based loading and normalized storage
#tanstack #react #tanstackdb #tanstackquery #FrontendBeer
Medium
TanStack DB Query: Step-by-step guide to combining parameter-based loading and normalized storage
In this guide, I will demonstrate how to use TanStack Query v5 and TanStack DB to achieve a seamless user interface with incremental…
🔥2❤1
Типовые ошибки при работе с TanStack DB
useLiveQuery возвращает `{ type: 'ref' }` вместо данных
Не выбирайте напрямую весь объект коллекции - это возвращает ссылочный proxy, и React не увидит изменения. Всегда явно перечисляйте поля, которые должны быть реактивными
Невозможно преобразовать объект в примитив
Поля коллекции являются прокси-объектами, поэтому Number(u.id) приводит к ошибке. Используйте функции (`eq`,` lt`,
DuplicateKeyInBatchError
Не удаляйте и не вставляйте запись с одним и тем же ключом в рамках одного
SyncNotInitializedError
Нельзя писать в коллекцию, пока она не готова. Перед записью убедитесь, что коллекция синхронизирована (`isReady()`), вызвав
Несоответствие типа ключа (‘42’ vs 42)
Если ключи числовые, используйте числа, а не строки. Например, приводите maybe String Id к числу перед вызовом usersCollection.get(id)
#tanstack #react #tanstackdb #FrontendBeer
useLiveQuery возвращает `{ type: 'ref' }` вместо данных
Не выбирайте напрямую весь объект коллекции - это возвращает ссылочный proxy, и React не увидит изменения. Всегда явно перечисляйте поля, которые должны быть реактивными
// Do not do this
// returns ref/proxy, UI does not see field changes
const { data: users = [] } = useLiveQuery((q) =>
q.from({ u: usersCollection }).select(({ u }) => u),
)
// Do this
// always explicitly list the fields for which you want reactivity.
const { data: users = [] } = useLiveQuery((q) =>
q.from({ u: usersCollection }).select(({ u }) => ({
first_name: u.first_name,
id: u.id,
last_name: u.last_name,
email: u.email,
})),
)
Невозможно преобразовать объект в примитив
Поля коллекции являются прокси-объектами, поэтому Number(u.id) приводит к ошибке. Используйте функции (`eq`,` lt`,
gt и пр.) из @tanstack/db для сравнения и фильтрации данных
// Do not do this
// u.id — it's a proxy; Number(u.id) will throw an error/give an incorrect result
const { data } = useLiveQuery((q) =>
q.from({ u: usersCollection })
.where(({ u }) => Number(u.id) > 10)
.select(({ u }) => ({ id: u.id })),
)
// Do this
// use builders from @tanstack/db: eq, lt, gt, inArray, between, etc.
import { gt } from '@tanstack/db'
const { data } = useLiveQuery((q) =>
q.from({ u: usersCollection })
.where(({ u }) => gt(u.id, 10))
.select(({ u }) => ({ id: u.id })),
DuplicateKeyInBatchError
Не удаляйте и не вставляйте запись с одним и тем же ключом в рамках одного
writeBatch. Разделите операцию на два батча (сперва удаление, затем вставка) или заранее удалите лишние ключи и затем вставьте новые данные
// Do not do this
// in ONE batch, delete and upsert by ONE id
usersCollection.utils.writeBatch(() => {
usersCollection.utils.writeDelete(42)
usersCollection.utils.writeUpsert({ id: 42, first_name: 'A', last_name: 'B', email: '[email protected]' })
})
// Do this
// split into two batches
usersCollection.utils.writeBatch(() => {
usersCollection.utils.writeDelete(42)
})
usersCollection.utils.writeBatch(() => {
usersCollection.utils.writeUpsert({ id: 42, first_name: 'A', last_name: 'B', email: '[email protected]' })
})
// Do this
// OR deduplicate in advance
usersCollection.utils.writeBatch(() => {
const incoming = new Map(dto.data.map((u) => [u.id, u]))
// remove old items which no in new data
for (const [k] of usersCollection.entries()) {
if (!incoming.has(k as number)) {
usersCollection.utils.writeDelete(k)
}
}
// insert / update with new items
for (const u of incoming.values()) {
sersCollection.utils.writeUpsert(u)
}
})
SyncNotInitializedError
Нельзя писать в коллекцию, пока она не готова. Перед записью убедитесь, что коллекция синхронизирована (`isReady()`), вызвав
startSyncImmediate() и дождавшись stateWhenReady()
// Do not do this
// direct entries before the collection was moved to ready
usersCollection.utils.writeUpsert(user)
// Do this
// guarantee ready (once at startup or before recording)
if (!usersCollection.isReady()) {
usersCollection.startSyncImmediate()
await usersCollection.stateWhenReady()
}
usersCollection.utils.writeUpsert(user)
Несоответствие типа ключа (‘42’ vs 42)
Если ключи числовые, используйте числа, а не строки. Например, приводите maybe String Id к числу перед вызовом usersCollection.get(id)
// Do not do this
// if you use a number keys, but you are searching for a string
usersCollection.get('42') // undefined
// Do this
// use one type of key and bring in the input
const id = Number(maybeStringId)
usersCollection.get(id)
#tanstack #react #tanstackdb #FrontendBeer
❤2🔥1👏1
Please open Telegram to view this post
VIEW IN TELEGRAM
Бывает, что нужно заменять данные в строке по шаблону, например для:
- API эндпойнта
/api/v1/resource/{id};- или просто строки вида
Hello {name}, you have {count}Так как такие строки могут быть объявлены в одном файле, а использоваться в других, то было бы здорово иметь возможность это делать безопасно на уровне типов. То есть вытащить:
{ id: string }, { name: string ; count: ... }, а если в строке нет плейсхолдеров, то ничего не вытаскиватьДля этого нам понадобится: template literal types, conditional types, inference и recursive type reference в TS
export type ExtractPlaceholdersType<S extends string> = S extends `${string}{${infer Parameter}}${infer Rest}`
? ExtractPlaceholdersType<Rest> | Parameter
: never
export type TemplateParametersType<S extends string, V = string> = {
[K in ExtractPlaceholdersType<S>]: V
}
export type TemplateRendererType = <S extends string>(
template: S,
...parameters: ExtractPlaceholdersType<S> extends never ? [] : [TemplateParametersType<S>]
) => stringЧто тут происходит:
-
ExtractPlaceholdersType рекурсивно собирает union из плейсхолдеров ("name" | "count" | ...)-
TemplateParametersType превращает union в объект-
TemplateRendererType делает параметр условно обязательным, если есть плейсхолдеры - надо передать объект, если нет - TS будет ругаться что переданы лишние аргументыНу и на десерт, реализация типизированной замены плейсхолдеров в строке (за быстро, то есть альтернативы через RegExp и пр. замены работают значительно медленнее):
// Маркеры плейсхолдера в шаблоне: {key}
const START = '{'
const END = '}'
// Длина маркера START (1), используем как сдвиг при вычислении индексов
const SHIFT = START.length
export const renderString: TemplateRendererType = (inputString, ...parameters) => {
// Быстрый выход: parameters всегда массив, если не передан - возвращаем исходную строку
if (parameters.length === 0) {
return inputString
}
// Возьмем только первый элемент из parameters
// Можно закастовать тип, так как передавать для замены будем <string, string> и это решается на уровне TemplateParametersType
const placeholdersMap = parameters[0] as Record<string, unknown>
// Быстрый выход: если в строке нет START, то плейсхолдеров нет
if (inputString.indexOf(START) === -1) {
return inputString
}
// Собираем результат по чанкам, двигаясь слева направо
let result = ''
let cursor = 0
const inputStringLength = inputString.length
// Основной проход по строке
// cursor монотонно растёт сканируем одну позицию один раз
while (cursor < inputStringLength) {
// Ищем очередное START начиная с cursor
const start = inputString.indexOf(START, cursor)
// Если START больше нет, то добавляем остаток строки и заканчиваем
if (start === -1) {
result += inputString.slice(cursor)
break
}
// Ищем закрывающие END после найденного START
const end = inputString.indexOf(END, start + SHIFT)
// Если закрывающих нет, то считаем оставшееся обычным текстом и возвращаем как есть
if (end === -1) {
result += inputString.slice(cursor)
break
}
// Добавляем текст по ключу между текущей позицией и началом плейсхолдера
result += inputString.slice(cursor, start)
// Достаём название плейсхолдера между START и END
const key = inputString.slice(start + SHIFT, end)
// Берём значение по ключу
const value = placeholdersMap[key]
// Если значение есть, то подставляем в результат
if (value != null) {
result += value
}
// Сдвигаем cursor на символ сразу после END
cursor = end + SHIFT
}
return result
}Ну и дальше можно просто использовать
renderString(`/api/resource/v1/{id}`, { id: '1' })
renderString('Hello {name}, you have {count} ...', { name: 'Random', count: '22' })
renderString('{a} + {a} = {b}', { a: '1', b: '2' }) // И так тоже работает, заменяя один и тот же ключ#typescript #string #FrontendBeer
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍2👀1
Друзья, с Новым годом! 🎄
Желаю в 2026 спокойных релизов без багов, чтобы все взлетало с первого раза🚀 Сил, здоровья и баланса в жизни. Пусть год будет тёплым, продуктивным и с хорошими новостями ✨
#FrontendBeer
Желаю в 2026 спокойных релизов без багов, чтобы все взлетало с первого раза
#FrontendBeer
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5