<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <language>ru</language>
    <title>Redux</title>
    <description/>
    <link>https://tproger.ru/tag/redux</link>
    <atom:link href="https://tproger.ru/tag/redux/feed" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Sat, 03 Oct 2026 22:12:30 GMT</lastBuildDate>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>12</sy:updateFrequency>
    <image>
      <title>Redux</title>
      <link>https://tproger.ru</link>
      <url>https://tproger.ru/apple-touch-icon.png</url>
    </image>
    <item>
      <title>30 дней: блочный конструктор README — один DOM, два хозяина</title>
      <link>https://tproger.ru/articles/30-dnej-blochnyj-konstruktor-readme-odin-dom-dva-hozyaina</link>
      <comments>https://tproger.ru/articles/30-dnej-blochnyj-konstruktor-readme-odin-dom-dva-hozyaina?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Islam Mada]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/30-dnej-blochnyj-konstruktor-readme-odin-dom-dva-hozyaina</guid>
      <description><![CDATA[<p>Блочный конструктор README-файлов написанный с нуля за 30 дней: кастомный WYSIWYG без сторонних редакторов, своя Markdown Object Model на TypeScript, contenteditable в связке с React, немного боли от браузерных API и ноль.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/30-dnej-blochnyj-konstruktor-readme-odin-dom-dva-hozyaina">30 дней: блочный конструктор README — один DOM, два хозяина</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Markdown]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 07 May 2026 03:15:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Мы живём в эпоху когда можно написать в чат «сделай мне CRUD» и получить рабочий код через десять секунд что в принципе удобно. И это, если честно, главная причина почему я периодически намеренно лезу в что-то сложное руками — чтобы не разучиться думать о том что происходит внутри.</p><p>ИИ я использую. Но в этом проекте он был исключительно быстрой документацией — особенно когда добрался до selection/range API, про которые до этого знал чуть меньше чем ничего. Реализация все равно была за мной.</p><p>Так вот — ReadGen. Блочный конструктор README-файлов. Месяц, 2-3 часа в день, React и TypeScript и небольшая пачка дополнительных библиотек для разумного облегчения жизни. Важно понимать что это <b>не коммерческий продукт и не претендует на решение чьей-то боли.</b> Просто техническая задача которую я давно хотел разобрать.</p><p>Демо:<a href="http://readgen-eight.vercel.app"> readgen-eight.vercel.app</a></p><p><a href="http://readgen-eight.vercel.app"></a>Репозиторий:<a href="https://github.com/islamxm/readgen"> github.com/islamxm/readgen</a></p><h2>Почему</h2><p>contenteditable — старый добрый дед среди браузерных API. То что он старый - это да, а вот доброта - под вопросом.</p><p>Он искренне хочет помочь, сам расставляет &lt;div&gt; и &lt;br&gt;, сам решает что делать с Enter, сам форматирует как считает нужным. Проблема в том что его никто не спрашивал. Когда тебе нужен предсказуемый блочный редактор с контролируемой структурой данных — такая самодеятельность становится основным источником боли.</p><p>Готовые текстовые движки эту боль скрывают. Я хотел её почувствовать лично. Понять где именно браузер начинает делать по-своему и что с этим делать. Это и был настоящий вопрос проекта.</p><p>Поначалу казалось что задача не супер сложная, но довольно быстро понял что полноценный текстовый редактор это бесконечные edge cases которые не влезут ни в какой месяц. Граница была проведена жёстко: плоская структура блоков, никакой иерархии. Всё остальное — потом, в другой жизни.</p><p>Так задача сузилась до честного и реалистичного объёма, блочный конструктор README без вложенности, с предсказуемым ядром и нормальным экспортом.</p><h2>Три OM</h2><p>Между реальным DOM и виртуальным DOM React затесался ещё один — MOM, Markdown Object Model. Пафосное название для того что по сути является flat map структурой документа. Но только по сути — на деле это целое ядро, состоящее из парсера, валидатора, гардов, движка, сериализатора. Это по сути модули без внешних зависимостей (кроме dexie.js в подмодуле storage) которые принимают данные, делают свое дело и отдают, впрочем как и принято в среде порядочных разработчиков.</p><p>Архитектурно MOM живёт как отдельный изолированный модуль — ведёт себя как shared слой в FSD, но это не набор утилит а самостоятельное ядро. Всё остальное приложение построено на гибриде FSD и layered подхода, но MOM существует по своим правилам.</p><h2>Один DOM, два хозяина</h2><p>Вот где настоящий конфликт. Структурой документа, порядком блоков, состоянием UI управляет React. Но внутри редактируемых блоков он полностью отступает. Там нет react рендеринга, только innerHTML и чистый DOM. Синхронизация происходит в эффекте, источник истины — содержимое contenteditable, и именно оттуда формируется MOM.</p><p>Два хозяина поделили территорию. React взял всё снаружи, DOM взял всё внутри. С одной стороны казалось бы все круто, так и должно быть, но на самом деле это порождает новый пласт проблем.</p><p>Отдельная история внутри этой истории — курсор. Позицию нужно постоянно сохранять и восстанавливать через selection/range API, и синхронизировать это с React. Это отдельная боль внутри общей боли.</p><p>Деструктивная синхронизация DOM — innerHTML = '' перед каждой манипуляцией выглядит грубо, зато работает. Можно было конечно написать свой reconciliation но учитывая исходные рамки по времени я решил отказаться.</p><h2>Генераторы в сериализаторе</h2><p>Экспорт документа в .md — финальная точка всей архитектуры. MOM → Serializer → файл. Впервые за все время своего осознанного программирования я решил тут применить генераторы в деле.</p><p>Каждый тип узла имеет свою функцию-генератор. Первый yield — контент блока, второй yield "" — пустая строка-разделитель. Тут мы избавляемся от аккумулятора который нужно тащить в глубь и код делаем более плоским и понятным. Array.from(gen).join("\n") в конце собирает всё в строку.</p><h2>Drag and drop на Pointer Events</h2><p>Кастомный drag and drop с анимацией как отдельный хук. И тут я познакомился с довольно таки мощными событиями класса pointer</p><p>о существовании которого я знал но почему то всегда обходился с touch</p><p>и <i>mouse</i> ивентами.</p><p>Pointer Events унифицируют всё что раньше требовало отдельных mouse и touch обработчиков. Без pointercancel drag зависает если браузер решает прервать жест сам. setPointerCapture — чтобы события не терялись когда курсор уходит за пределы контейнера.</p><h2>Компромиссы</h2><p>MOMRaw — честное признание границы. Все что не попало в текущую версию движка (таблицы, инлайн-картинки, инлайн-код и т.д.) живёт в одном компромиссном блоке. Вставляй сырой Markdown или HTML, смотри превью через внешнюю библиотеку в изолированном Shadow DOM.</p><p>То же самое с блоком кода - MOMCode. Для этого я подключил библиотеку uiw, думаю в будущем заменю его на более современное и понятное решение.</p><p>Виртуализации нет, README на 10 000 блоков не существует в природе, проблема надуманная ну или просто не успел (выбирайте сами).</p><p>Мелочи которые просто есть и работают: менеджер глобальных шорткатов, тултипы для вставки блоков, эмодзи и форматирования текста, миниатюры документов через modern-screenshot, хранение в IndexedDB без бэкенда.</p><h2>Заключение</h2><p>Месяц закончился. contenteditable я теперь знаю лично — и он знает меня. Selection/Range из страшного незнакомца превратился в рабочий инструмент. Понял где React уместен, а где лучше отойти и дать DOM делать своё дело. Нашёл наконец задачу где генераторы оказались не паттерном ради паттерна а реально лучшим решением.</p><p>Демо:<a href="http://readgen-eight.vercel.app"> readgen-eight.vercel.app</a></p><p><a href="http://readgen-eight.vercel.app"></a>Репозиторий:<a href="https://github.com/islamxm/readgen"> github.com/islamxm/readgen</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Типизированная навигация в React Router</title>
      <link>https://tproger.ru/articles/tipizirovannaya-navigaciya-v-react-router</link>
      <comments>https://tproger.ru/articles/tipizirovannaya-navigaciya-v-react-router?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Михаил Сахаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/tipizirovannaya-navigaciya-v-react-router</guid>
      <description><![CDATA[<p>Когда прочитаете эту статью, сможете настроить типобезопасную навигацию в своем проекте, забудете про сломанные ссылки после рефакторинга и перестанете нервничать на релизах.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/tipizirovannaya-navigaciya-v-react-router">Типизированная навигация в React Router</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 16 Jul 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Типизированная навигация в React Router решает классические проблемы фронтенд-разработки: опечатки в путях, сломанные ссылки после рефакторинга и отсутствие автокомплита. Это полезно и джунам, которые хотят избежать глупых ошибок, и сеньорам, проектирующим большие проекты. Инструмент превращает строковые пути в типобезопасную систему навигации.</p><p>Представьте: пятница, 18:30. Релиз через час. Вы меняете один роут в конфиге — и внезапно половина приложения отлетает. Поздравляем, вы только что познакомились с классической болью фронтендеров.</p><p>Проблема кроется в самой природе JavaScript. Строковые пути вроде /users/profile/${id} существуют в коде как обычные строки — без проверок, автокомплита и гарантий корректности. Опечатался в /usres вместо /users — и твоя навигация сломалась, а TypeScript молчит как рыба.</p><p>Двадцать лет назад мы кликали по window.location.href, десять лет назад — радовались React Router, а сегодня пора переходить на типизированные решения.</p><p>Когда прочитаете эту статью, сможете настроить типобезопасную навигацию в своем проекте, забудете про сломанные ссылки после рефакторинга и перестанете нервничать на релизах.</p><h2>Суть проблемы</h2><p>Проблема очевидна: путь /admin/products размазан по всему коду. TypeScript не знает, что эти строки связаны с определенной директорией, поэтому не проверяет их корректность. Опечатка в product вместо products — и вылетает ошибка 404.</p><p>В больших проектах эта проблема критична. Приложение с 200+ путями, где навигация разбросана по сотне компонентов, превращается в минное поле. Один программист меняет структуру URL, а остальные даже не подозревают об этом. Команды используют TypeScript для типобезопасности, но навигация остается уязвимой.</p><h2>Что такое типизированная навигация?</h2><p>Типизированная навигация превращает строковые пути в типизированные объекты. Вместо /admin/products/123/edit программист работает с функциями, которые знают структуру приложения и проверяют корректность написания путей на этапе компиляции.</p><p>Представьте GPS-навигатор, который знает все адреса в городе. Вы не можете ввести несуществующую улицу — система сразу выдаст ошибку. Так работает типизированная навигация: TypeScript проверяет, что путь существует, параметры переданы правильно, а структура URL соответствует пути.</p><p>Три ключевых преимущества: автокомплит в IDE, проверка на этапе компиляции и безопасный рефакторинг. Поменяете пути в коде — TypeScript сразу покажет все места, которые нужно обновить.</p><p>Польза зависит от уровня разработчика:</p><ul><li>Джуны получают защиту от опечаток и автокомплит — меньше глупых ошибок и быструю разработку.</li><li>Миддлы ускоряют разработку благодаря надежному рефакторингу — можно смело менять структуру URL без страха что-то сломать.</li><li>Сеньоры используют типизацию для построения архитектуры приложения — создают переиспользуемые компоненты навигации, проверяют параметры и строят масштабируемые системы путей и директорий.</li></ul><p>Типизированная навигация превращает хрупкий код в надежную систему, где ошибки находятся до деплоя.</p><h2>Как использовать React Router вместе с TypeScript</h2><p>Для базовой типизации в React Router v6 сначала определите структуру путей. Создайте интерфейс, который описывает все пути в приложении:</p><p>Типизация параметров URL решает проблему с useParams. Вместо any получаете конкретные типы:</p><p>Query-параметры типизируются аналогично через useSearchParams. Создайте интерфейс для каждой страницы с query-параметрами и оберните хук.</p><p>Так система будет дополнять код в IDE, проверять все пути на этапе компиляции и защитит от опечаток. Полчаса настройки сэкономят вам часы отладки и целый вагон нервов.</p><h2>Как внедрить типизированную навигацию в проекты</h2><p>Централизованная система маршрутов облегчает управление навигацией в больших приложениях. Создайте отдельный файл с конфигурацией всех путей:</p><p>Конфигурация избавит от нужды дублировать код при генерации типов. TypeScript автоматически выведет все возможные пути и их параметры из одного объекта.</p><p>Современные библиотеки решают проблему из коробки.<a href="https://tanstack.com/router"> Например, Tanstack Router</a> предоставляет полностью типизированную систему путей с автогенерацией типов, а<a href="https://github.com/typehero/type-route"> Type-route</a> создает типобезопасные пути через API.</p><p>Библиотека<a href="https://github.com/typesafe-routes/typesafe-routes"> typesafe-routes</a> внедряется даже в крупные проекты без необходимости менять сотни строк кода.</p><p>Выбор инструмента зависит от размера программы. Если у вас небольшое приложение — быстрее написать хук в пару строк. Но если разрабатываете сложный сервис — используйте библиотеки.</p><h2>Как не сломать код при использовании типизированной навигации</h2><p>Не пытайтесь переписать весь проект за раз — создайте типизированные хуки для новых фич, а старый код обновляйте по мере рефакторинга.</p><p>Чеклист для код-ревью поможет не уронить прод:</p><ul><li>Все новые navigate() и  используют типизированные версии;</li><li>Параметры путей явно типизированы;</li><li>Нет магических строк в навигации;</li><li>Query-параметры описаны интерфейсами.</li></ul><p>Важно: не используйте одновременно строки и типизированные пути — выберите один подход для проекта и не допускайте высокого уровня вложенности в объектах.</p><p>Автоматизация упрощает процесс. ESLint правило no-hardcoded-routes запретит использование строк в навигации. Код-генераторы создают типы из OpenAPI схем или конфигурации роутера.</p><p>Производительность не страдает — типы исчезают после компиляции. Теряется немного времени на компиляцию TypeScript, но экономия на отладке перекрывает затраты.</p><h2>Где применять типизированную навигацию</h2><p>SPA-приложения получают максимальную пользу от типизированных путей. В дашбордах с десятками страниц навигация становится еще важнее — один сломанный путь может уронить проект.</p><p>E-commerce проекты выигрывают от типизации каталогов и фильтров. Пути вроде /catalog/:category/:subcategory?filters=price,brand содержат много параметров, которые легко сломать при рефакторинге.</p><p>Интеграция с Redux и Zustand упрощает синхронизацию состояния с URL. Типизированные селекторы автоматически обновляются при изменении роутов:</p><p>Вложенные директории требуют особого внимания. Каждый уровень вложенности усложняет типизацию — планируйте структуру заранее, а не рефакторьте задним числом.</p><p>Типизированная навигация — стандарт современной разработки. Команды, которые до сих пор полагаются на строковые пути, тратят лишнее время на поиск багов. Программисты увереннее рефакторят код, не боятся мелких ошибок и не роняют прод в пятницу вечером из-за одного символа в пути.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как TanStack Query ускоряет работу с API и сокращает код</title>
      <link>https://tproger.ru/articles/kak-tanstack-query-uskoryaet-rabotu-s-api-i-sokrashhaet-kod</link>
      <comments>https://tproger.ru/articles/kak-tanstack-query-uskoryaet-rabotu-s-api-i-sokrashhaet-kod?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дмитрий Скляр]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-tanstack-query-uskoryaet-rabotu-s-api-i-sokrashhaet-kod</guid>
      <description><![CDATA[<p>Использование TanStack Query дает разработчикам возможность упростить работу с API, сократить дублирование кода и ускорить разработку. Рассказываем о проблемах, связанных с использованием API, и соответствующих решениях для повышения эффективности разработки.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-tanstack-query-uskoryaet-rabotu-s-api-i-sokrashhaet-kod">Как TanStack Query ускоряет работу с API и сокращает код</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Xen]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 28 May 2025 14:00:04 GMT</pubDate>
      <content:encoded><![CDATA[<p><i>Представьте: вы — фронтенд-разработчик, и постоянно сталкиваетесь с рядом проблем. Еще один endpoint, еще один запрос, еще десяток строк почти идентичного кода. Вручную прописываете типы, парсите ответы, обрабатываете ошибки, обновляете кэш… А через неделю бэкенд-команда меняет API — и все, что вы строили, рассыпается как карточный домик.</i></p><p><i>Так случалось, пока не появились инструменты вроде <a href="https://tanstack.com/query/latest">TanStack Query</a> и не родились «обертки», которые уменьшают объемы рутины. Как перестать тонуть в запросах на разработку API и начать дышать свободно, рассказывает Дмитрий Скляр, старший разработчик компании Axenix. </i></p><h2>API: нервная система цифрового мира</h2><p>В современном ИТ-ландшафте API давно перестал быть просто техническим термином. Теперь он — один из столпов, на котором держится цифровая цивилизация.</p><p>Философия современной разработки давно сместилась от принципа «сделай все сам» к парадигме «собери из лучших компонентов». API стал языком, на котором взаимодействуют цифровые сервисы. Он превратил ИТ-ландшафты и интернет из собрания разрозненных приложений и сайтов в единую живую экосистему, где каждый элемент может взаимодействовать с другим.</p><p>Когда разработчик использует API картографического сервиса, ему не нужно разбираться в геоданных или алгоритмах прокладки маршрутов — он просто отправляет запрос и получает готовое решение. Это похоже на то, как мы пользуемся электричеством: не нужно понимать, как работает электростанция, чтобы включить свет в комнате.</p><p>Но настоящую революцию API совершил в бизнесе, создав целые экосистемы цифровых услуг. Такие компании, как Stripe, Twilio или Plaid построили свои империи именно на API, предоставляя другим разработчикам готовые «кирпичики» для создания финансовых, коммуникационных или аналитических сервисов.</p><p>Внутри крупных компаний API выступают в роли «дипломатов» между микросервисами и ИТ-системами, позволяя разным командам работать независимо, но при этом сохранять общую согласованность. Когда маркетинговая система запрашивает данные о продажах, а сервис логистики получает информацию о новых заказах — все это происходит через четко определенные API-контракты, которые делают сложные системы управляемыми и гибкими.</p><h2>Боль, которую никто не замечает</h2><p>Но не все так просто и далеко не так радужно.</p><p>Да, формально API — это универсальный мост, например, между фронтендом и бэкендом. На практике же он часто напоминает шаткую подвесную конструкцию: данные приходят в разном формате, документация устаревает еще до релиза, а поля user_name и username существуют одновременно просто потому, что «исторически сложилось».</p><p>Типичный сценарий: вы пишете код для запроса списка товаров, все типизируя и описывая модели — 50 строк. Добавляете фильтрацию — еще 30 строк. А через месяц бэкенд меняет структуру ответа, забывает предупредить — и вы тратите день на поиск багов в трех разных местах.  И самое обидное: 80% этого кода — копипаст. Проверка ошибок, трансформация данных, инвалидация кэша — все одно и то же, но плодится как вирусы.</p><p>Сюда можно добавить также сложность поддержки — API меняется, код устаревает.</p><p>Другой момент: код для каждого API-метода имеет схожую структуру. Повторяются основные шаблоны, а различия лишь в типах данных и отдельных деталях. Чем больше API-методов, тем сложнее отслеживать изменения в коде и поддерживать его в актуальном состоянии.</p><p>В итоге — дублирование кода, которое усложняет приложение, снижает его читаемость и замедляет разработку новых функций. Документация? Либо устарела, либо ее вообще не писали.</p><h2>Спасение — в системе</h2><p>Однажды наши разработчики устали писать однотипные хуки, чинить сломанные запросы и объяснять новичкам, что где находится, почему именно так это работает и почему нельзя просто взять и использовать что-то другое. Устали писать однотипный код для каждого запроса, захотели минимизировать ошибки и ускорить разработку, а также сделать работу с API более структурированной и понятной. Также у команд назрела потребность в скорейшем включении в проект новых разработчиков. Для всех этих задач подходит одно решение: унифицированный подход к API, который также наводит порядок в данных и отчетах, упрощая аналитику и логику приложения.</p><p>Тогда и родилась идея фабрики API — слоя абстракции, который скрывает рутину. Для этого мы привлекли возможности <a href="https://tanstack.com/query/latest">TanStack Query</a>, семейство библиотек для управления состоянием данных (data fetching) в клиентских приложениях. Помимо <a href="https://tanstack.com/query/latest">TanStack Query (ранее React Query)</a>, к этому классу инструментов также относятся: RTK Query (из Redux Toolkit), Apollo Client (для GraphQL) и SWR.</p><p>Их общая философия: «делать рутину невидимой для разработчика». Рассмотрим Конкретные проблемы и их решения.</p><h3>Однотипные хуки для каждого запроса</h3><p>В большинстве проектов без дополнительной абстракции каждый хук под API-запрос пишется вручную. Меняется только URL и параметры, а структура остаётся одинаковой: queryKey, queryFn, опции запроса. Это быстро приводит к копипасту, дублированию логики и усложнению поддержки.</p><p>Например, для каждого ресурса вроде пользователей, продуктов, заказов и т.д. приходится повторять одну и ту же конструкцию. Если нужно изменить поведение запроса (например, добавить retry или staleTime), правки необходимо делать в десятках мест.</p><p><b>Решение: Универсальная обёртка над хуками TanStack Query</b></p><p>Создание единой функции-генератора для хуков позволяет избавиться от повторяющегося кода. Она принимает ключ, функцию запроса и опциональные параметры — и возвращает сразу «пачку» готовых хуков.</p><p>Такой подход:</p><ul><li>снижает количество шаблонного кода;</li><li>упрощает масштабирование;</li><li>централизует поведение всех запросов;</li><li>позволяет быстро адаптироваться к изменениям (например, добавить логирование, типизацию, трансформации и т.п.).</li></ul><h3>Хрупкость при изменении API</h3><p>В типичном приложении без централизованной трансформации мы напрямую используем ответ от бэкенда. Любое изменение формата требует правок в типах, в местах использования данных, и часто приводит к багам.</p><p><b>Решение: Централизованная трансформация данных +</b> <a href="https://github.com/typestack/class-transformer">class-transformer</a>.</p><p>С помощью <a href="https://github.com/typestack/class-transformer">class-transformer</a> можно объявить классы сущностей и задать правила преобразования один раз.</p><p>Плюсы:</p><ul><li>Все данные автоматически приходят в нужном виде;</li><li>Компоненты работают с гарантированно типизированными данными;</li><li>Один источник правды: при изменении API – правим только Entity-класс;</li><li>Удобно масштабируется, особенно если API большое и сложное.</li></ul><h3>«Мусор» в данных и отчётах</h3><p><b>Решение: Валидация данных при трансформации (например, через class-validator); автоматическая синхронизация кеша (актуальные данные во всём приложении).</b></p><p>Почему это лучше ручного подхода? Смотрите: строк кода на 1 endpoint при ручном управлении <b>потребуется 30+</b>, а <b>с фабрикой —  5-10</b>. Времени для добавления нового поля —  1<b> час вручную, с фабрикой —  5 минут</b>. Количество мест для правки при изменении API —  вручную их много, с фабрикой — лишь одно.</p><p>Реальный кейс: в проекте с 50+ endpoint’ами переход на фабрику <b>сократил код на 70%</b>. Разработчики перестают быть «переводчиками» между API и интерфейсами сервисов и приложений, а сосредотачиваются на бизнес-логике. Как сказал один тимлид: <i>«Теперь мы не фиксим баги данных, а делаем фичи, которые нравятся пользователям»</i>.</p><p>Еще пример: вместо пяти отдельных хуков для CRUD-операций фабрика дает одну функцию createApi(). Вместо ручного парсинга — автоматическую трансформацию данных через <a href="https://github.com/typestack/class-transformer">class-transformer</a>. А главное — единые правила игры для всего проекта.</p><p>Как это работает? Представьте, что вы говорите системе: «Вот endpoint для товаров, вот их модель данных, вот правила валидации» —  а все остальное она делает за вас. Хотите получить товар по ID? Пишете useGetByIDQuery. Нужно обновить? —   useUpdatetMutation. И никакого шаманства с ручным описанием каждого хука.</p><p>Но главное — когда бэкенд меняет API, правки нужны только в одном месте. А новые разработчики перестают спрашивать: <i>«Почему у нас три разных способа загрузить список пользователей?»</i>.</p><h2>Как договориться и не сойти с ума</h2><p>Проблема в том, что без четкого контракта, набора правил и подходов фронтенд- и бэкенд-разработчики живут в параллельных реальностях. Один думает, что данные придут в camelCase, другой шлет их в snake_case. Один ожидает массив, другой неожиданно подсовывает null. Итог —  бесконечные баги, исправления «на живую» и испорченные нервы.</p><p>Решение? Четкий контракт. Простой, прозрачный, однозначный. В нем должны быть:</p><ol><li>Единые правила именования — если бэкенд отдает snake_case, фронтенд не должен гадать, будут ли остальные поля в camelCase, или, например, если в одной модели данных full_name , в другой не будет fullname и так далее.</li><li>Единый формат запросов и ответов.</li><li>Договоренности о структуре URL, формате данных и кодах ошибок.</li><li>Строгая типизация — TypeScript-интерфейсы, которые знают, какие поля обязательны, а какие могут отсутствовать.</li><li>Документация, которая не врет — если Swagger говорит, что поле email есть, оно должно быть. Всегда.</li></ol><p>И самое главное — этот контракт должен соблюдаться. Если бэкендеры меняют API, они обязаны предупредить. Иначе фронтенд превращается в сапера, который каждое утро разминирует прод.</p><p>Но, как правило, контракт сделать тяжело.  Каждый видит REST по-своему: разные URL, форматы данных, обработка ошибок. Модели данных непоследовательны — поля то есть, то их нет, вложенность меняется. Документации либо нет, либо она устарела, так что API изучаем методом проб и ошибок. От такого надо отказываться сразу и стараться договорится на берегу.</p><p>Для унификации и строгого соответствия данных мы используем <a href="https://github.com/typestack/class-transformer">class-transformer</a>: автоматически приводим данные к нужным форматам, вместо работы с сырыми JSON-объектами. Так получаются экземпляры классов с методами и свойствами. Далее убираем ручную обработку и проверки данных, преобразовываем вложенные структуры и применяем кастомные трансформации.</p><h4>Что получаем в итоге?</h4><p>Когда контракт есть, а обертка API готова, магия начинает работать:</p><ul><li><b>Простота использования</b>: вместо десятков хуков — единая фабрика createApi(). Меньше boilerplate-кода.</li><li><b>Мощные возможности</b>: данные приходят уже в нужном формате без ручных проверок. Кэш, инвалидация и оптимизации —  <a href="https://tanstack.com/query/latest">TanStack Query</a> делает за вас всю грязную работу.</li><li><b>Новички влетают в проект: </b>больше не нужно объяснять, почему useGetEntity в одном компоненте работает не так, как в другом.</li><li><b>Активное сообщество: </b><a href="https://tanstack.com/query/latest">TanStack Query</a> —  тысячи разработчиков, готовых ответить на вопросы. Здесь можно найти примеры для любых кейсов: от интеграции с <a href="https://nextjs.org/">Next.js</a> до кастомного кеширования.</li><li><b>Нет ограничений по использованию: </b><a href="https://tanstack.com/query/latest">TanStack Query</a> —  не только для React, есть версии для <a href="https://tanstack.com/query/latest/docs/framework/vue">Vue</a>, <a href="https://tanstack.com/query/latest/docs/framework/svelte">Svelte</a> и даже <a href="https://tanstack.com/query/latest/docs/framework/solid">Solid.js</a>. Также работает с любым API — REST, GraphQL, WebSockets.</li><li><b>Работа с SSR без боли: </b>готовая интеграция с <a href="https://nextjs.org/">Next.js</a>, <a href="https://remix.run/">Remix</a> и другими фреймворками. При этом данные, полученные на сервере, автоматически передаются на клиент. <a href="https://tanstack.com/query/latest">TanStack Query</a> синхронизирует серверный и клиентский рендеринг.</li></ul><p>Но есть и ложка дегтя. Отладка усложняется, если что-то сломается внутри обертки — придётся копать глубже. Возникает зависимость от библиотек: <a href="https://github.com/typestack/class-transformer">class-transformer</a>, axios и сам <a href="https://tanstack.com/query/latest">TanStack Query</a> становятся обязательными.</p><p>Однако игра стоит свеч. Потому что время, сэкономленное на рутине, можно потратить на то, что действительно важно —  фичи, которые понравятся пользователям, а не бесконечные правки API-вызовов.</p><h4>Сравнительные примеры</h4><p>GET /products — Список продуктов</p><p>GET /products/:id — Один продукт по id</p><p>POST /products — Создание продукта</p><p>PUT /products/:id — Обновление продукта по id</p><p>DELETE /products/:id — Удаление продукта по id</p><h4>Как это выглядит в обертке</h4><h4>Все свойства и возвращаемые хуки и конфиги из фабрики:</h4><h4>Пример использования в компонентах:</h4><h4>Использование конфигов:</h4><h4>Для сравнения с классическим решением:</h4><figure><img src="https://media.tproger.ru/user-uploads/115291/2025-05-22/efa40892-0692-4a19-9796-8f993267a22d.png" alt="Сравнение обычного использования и фабрики" /><figcaption>Сравнение обычного использования и фабрики</figcaption></figure><h2>Когда стоит переходить на обертку?</h2><p>Не каждый проект нуждается в таком подходе к API. Если у вас два-три endpoint’а и они никогда не меняются — возможно, обертка будет избыточной. Но представьте стартап, где каждый месяц добавляются новые сущности: сначала товары, потом отзывы, потом промокоды, рекомендации, аналитика.</p><p>Вот где система раскрывается на полную! Новая сущность? Пять минут на добавление — и готовы все CRUD-операции. Изменился бэкенд? Правим в одном месте — и все работает.  Пришел новый разработчик? Он не тратит неделю на изучение особенностей API.</p><p>Правда, если бэкенд живет в мире хаотичных endpoint’ов (например, GET /fetch_items, но DELETE /removeProduct), обертка не спасет.</p><h4>Но как навести порядок?</h4><p>Главный секрет — общаться. Не ждать, пока API сломается, а сразу договориться:</p><ul><li>Какие будут названия полей (created_at vs createdAt);</li><li>Как структурированы ошибки;</li><li>Когда и как можно менять контракт.</li></ul><p>Как сказал один разработчик: «фронтенд и бэкенд — как соседи по коммуналке. Можно ругаться из-за бардака на кухне, но лучше сесть и написать правила совместного проживания».</p><p>А напоследок —  график, для закрепления разницы между работой с оберткой и без нее.</p><figure><img src="https://media.tproger.ru/user-uploads/115291/2025-05-22/5f23aca4-8353-4ff4-8923-f24685395bb2.png" alt="График сравнительного примера" /><figcaption>График сравнительного примера</figcaption></figure>]]></content:encoded>
    </item>
    <item>
      <title>Как упростить работу с API в React-приложении с помощью RTK Query и OpenAPI?</title>
      <link>https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-</link>
      <comments>https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Державин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-</guid>
      <description><![CDATA[<p>Узнайте, как упростить работу с API в React-приложении с помощью RTK Query и OpenAPI: генерация запросов, типизация и меньше ручной работы.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-">Как упростить работу с API в React-приложении с помощью RTK Query и OpenAPI?</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 23 May 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Ручная интеграция API и веб-приложения часто приводит к проблемам с обратной совместимостью контрактов. На стороне фронтенда это приводит к падению приложения, на бекенде — к потере данных с фронтенда. Один из способов предотвратить такие сложности — кодген.</p><p>В статье мы разберёмся, как запустить кодген на основе OpenApi схемы для веб-приложения на стэке React + redux-toolkit.</p><p>Для справки: контракт — соглашение о формате данных между клиентом и сервером</p><h2>Какие проблемы решает кодген?</h2><p>Любой разработчик может совершить ошибку: неудачно разрезолвить мердж-конфликт, забыть предупредить о рефакторинге или банально опечататься.</p><p>В микро-сервисной архитектуре цена таких ошибок высока — оперативно доставить обновления многочисленным клиентам иногда бывает проблематично.</p><p><i>Кодген снижает вероятность таких ошибок, так как методы статично создаются на основе схемы, которая, в идеале, также должна генерироваться на основе кода бекенда. Рассмотрим подробнее.</i></p><h3>Синхронизация контрактов</h3><p>При активной разработке бекенд постоянно обновляет контракты. Без кодгена все обновления на стороне фронтенда синхронизируются вручную.</p><p>Также на основе контрактов создаются побочные интерфейсы, например, для redux-слайсов и react-компонентов. Ошибка в основном контракте создает цепочку проблем со связанными типами и интерфейсами.</p><p>Сочетание кодген-контрактов и typescript-утилит для создания побочных интерфейсов делает доставку обновлений простой и безопасной.</p><h3>Устранение дубликатов</h3><p>Когда проект не имеет четкой структуры, контракты оказываются разбросаны по всей кодовой базе. Разработчики, не сумев найти нужный интерфейс, просто создают свои варианты контрактов. Так рождаются дубликаты и несогласованность.</p><p><i>Кодген решает эти проблемы, создавая единую точку входа для всех методов и интерфейсов.</i></p><p><b>Совет:</b> Кодген проще и безопасней внедрять на начальных этапах разработки. А в уже написанных проектах — стоит внедрять его постепенно, например, по несколько эндпойнтов за раз.</p><h2>Что понадобиться для кодгена?</h2><p>Прежде чем запускать кодген нам нужно минимально настроить проект:</p><h3>Подготовить схему</h3><p>OpenAPI-схема в формате yaml:</p><p><b>Важно: </b>Любая ошибка в схеме приводит к поломке кодгена. Поэтому важно, чтобы ваша схема проходила валидацию.</p><p>Проверить валидность OpenAPI схемы можно на в <a href="https://editor.swagger.io">swagger редакторе</a></p><h3>Настроить Redux</h3><p>Redux-клиент для API-запросов:</p><p>Redux-слайс для хранения данных из API-запроса:</p><p>Redux-хранилище с подключенным API-клиентом и редюсером:</p><h3>Настроить кодген</h3><p>Для кодгена возьмём официальную библиотеку от Redux — <a href="https://www.npmjs.com/package/@rtk-query/codegen-openapi">@rtk-query/codegen-openapi</a></p><p><a href="https://www.npmjs.com/package/@rtk-query/codegen-openapi"></a>Затем создадим файл настройками кодгена:</p><p>Библиотека хороша тем что покрывает все основные потребности, позволяя быстро запустить когден без долгих настроек. Цена удобства — отсутствие гибкости. Адаптировать результат когдена под сложный кодстайл будет проблематично.</p><p>Если вам нужна гибкость, попробуйте использовать <a href="https://www.npmjs.com/package/@openapitools/openapi-generator-cli">@openapitools/openapi-generator-cli</a> с различными готовыми шаблонами или <a href="https://github.com/orval-labs/orval">Orval</a> от tanstack</p><h2>Запуск кодгена</h2><p>Для запуска добавим команду в package.json:</p><p>Далее заходим в корень проекта и запускаем команду:</p><p>На выходе должен получиться файл codegenApi.ts с содержимым:</p><p>Проверяем результат:</p><ul><li>Инджект кодген эндпойтов в baseApi;</li><li>Системные типы Props Response Error;</li><li>Контракт User;</li><li>RTK-query хуки.</li></ul><p><b>Совет: </b>создание кодгена можно автоматизировать, например, сделать его отдельным этапом в CI/CD, который выполняется прямо перед билдом приложения.</p><h2>Возможные проблемы</h2><h3>Необычные типы данных</h3><p>При внедрении кодгена в проект на Kotlin я столкнулся с проблемой парсинга: кодген не мог распарсить тип данных LocalDateTime, который должен возвращать строку с датой в формате ISO. Вместо строки когден возвращал пустой объект.</p><p>LocalDateTime — это класс из Java-библиотеки java.time, который представляет дату и время без учёта временной зоны.</p><p>Данную проблему я решил с помощью кодмода:</p><p>Кодмод запускается после команды codegen. Он бежит по файлу сверху вниз, находит нужный тип и меняет его значение на string.</p><p><b>Совет:</b> При добавлении кодмода в общий файл обязательно опишите проблему которую он решает. Когда кодмодов станет много и они начнут конфликтовать, вам будет проще разобраться в их работе.</p><p>Кодмоды отлично генерируются с помощью AI.</p><h2>Плюсы и минусы кодгена</h2><p>Плюсы:</p><ul><li>Генерируем API-слой всего приложения за секунды;</li><li>Получаем автоматическое строгое создание интерфейсов и типов;</li><li>Избавляемся от опечаток в URL и параметрах;</li><li>Обнаруживаем проблемы с обратной совместимостью контрактов на ранних этапах сборки;</li><li>Экономим время.</li></ul><p>Минусы:</p><ul><li>Качество кодгена зависит от качества описания OpenAPI схемы;</li><li>Если схема не генерируется в автоматическом режиме, актуальности OpenAPI схемы потребуется поддерживать вручную;</li><li>Логику нестандартных запросов придётся описывать вручную.</li></ul><p><b>Напоминание:</b> API-слой сложного проекта невозможно покрыть кодогенерацией на 100% — это нормально. Будьте готовы при помощи методов enhanceEndpoints и injectEndpoints комбинировать когден-эндпойнты с эндпойнтам написанными вручную</p><h2>Заключение</h2><p>Кодген ускоряет разработку и делает доставку изменений с бэкенда более безопасной, однако подход требует качественной спецификации, поэтому если у вас небольшой проект — скорее всего кодген для вас будет лишним усложнением.</p><p>В остальных случаях, используя кодген, вы получите предсказуемый и масштабируемый API-слой, который сэкономит сотни часов разработки и поможет вашей команде сместить фокус c рутинных задач на создание качественной бизнес-логики для вашего веб-приложения.</p><p>Ускоряй работу, автоматизируй рутину, используй лучшие тулзы. Все самые полезные инструменты <a href="https://t.me/+iKEwDxvulHFkZDhi">тут</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Большой гайд по React от Tproger: топовые статьи и инструменты</title>
      <link>https://tproger.ru/articles/bolwoj-gajd-po-react-ot-tproger--topovye-stati-i-instrumenty</link>
      <comments>https://tproger.ru/articles/bolwoj-gajd-po-react-ot-tproger--topovye-stati-i-instrumenty?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Анна Ельцова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/bolwoj-gajd-po-react-ot-tproger--topovye-stati-i-instrumenty</guid>
      <description><![CDATA[<p>Гайд по реакт. Топовые статьи и инструменты. React для новичков. Делимся теорией и практическими рейсами. Tproger. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/bolwoj-gajd-po-react-ot-tproger--topovye-stati-i-instrumenty">Большой гайд по React от Tproger: топовые статьи и инструменты</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Windows]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[Django]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Flutter]]></category>
      <category><![CDATA[Пет-проект]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 01 May 2025 10:00:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>Внутри этого гайда — самые топовые статьи от Tproger, c помощью которых вы сможете прокачать свои навыки: от базовых паттернов до сложных анимаций и бэка. Сначала немного разберемся в теории, а потом — перейдем к практике и реальным проектам, которые реализовали наши читатели. Не забудьте сохранить гайд!</p><h2>Немного базы</h2><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-05-05/08ef6c61-544e-4c5a-91dc-d0d4d3200357.jpg" alt="" /></figure><p>В этих статьях рассказываем о том, что нужно знать новичкам и не только.</p><ul><li><a href="https://tproger.ru/articles/reactjs-na-izi--chto-realno-nuzhno-znat-frontend-razrabotchiku-v-2025-godu">ReactJS на изи: что реально нужно знать фронтенд-разработчику в 2025 году</a> — помогаем новичкам войти войти и показываем опытным разработчикам, что они могли упустить.</li><li><a href="https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1">Какие есть паттерны в React и для чего они нужны: часть 1</a> и <a href="https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-2">Какие есть паттерны в React и для чего они нужны: часть 2</a> — здесь объясняем популярные архитектурные паттерны в React, когда и зачем их применять.</li><li><a href="https://tproger.ru/articles/luchwie-biblioteki-dlya-animacij-na-react">7 библиотек для анимаций на React / Tproger</a> — собрали актуальные библиотеки для анимации в React; сравнение, демо и рекомендации.</li><li><a href="https://tproger.ru/translations/mistakes-junior-react-developers-make">Типичные ошибки джунов, использующих React</a> — показываем типичные ошибки junior-разработчиков в React, рассказываем, как писать чище и стабильнее.</li><li><a href="https://tproger.ru/articles/podgotovka-okruzhenija-react-prilozhenija-vscode-prettier-eslint-stylelint-husky">Подготовка окружения React-приложения: VSCode, Prettier, ESLint, Stylelint, Husky</a> — рассказываем, как начать работу с приложениями на Реакте и что должно быть на рабочем столе.</li><li><a href="https://tproger.ru/articles/react-native-protiv-flutter--chto-luchwe">React Native против Flutter: что лучше</a> (для общего развития) — сравниваем популярные фреймворки для кроссплатформенной разработки; плюсы, минусы и рекомендации.</li><li><a href="https://tproger.ru/articles/deeplink-v-react-native--polnoe-rukovodstvo">Deeplink в React Native: Полное руководств для новичков</a> — рассказываем, что такое deeplink и как их писать на Реакте.</li></ul><h2>Практикуемся</h2><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-05-05/d2d4a20e-2de7-46d4-8b4d-bbf28558d7e6.jpg" alt="" /></figure><ul><li><a href="https://tproger.ru/articles/js--dran-and-drop--delaem-podvizhnye-bloki">Современный Drag and Drop в React с помощью dndkit: создаем перетаскиваемое меню</a> — здесь о том, как сделать меню, кнопки и все с помощью dndkit.</li><li><a href="https://tproger.ru/translations/django-react-webapp">Пишем приложение с бэкендом на Django и фронтендом на React</a> — рассказываем, как создать REST API на Джанго, добавить React и объединить это в один проект.</li><li><a href="https://tproger.ru/articles/sozdanie-todo-list-beskonechnoj-vlozhennosti-react-typescript-mobx">Создание ToDo-листа бесконечной вложенности на React, TypeScript и MobX</a> — пошагово создаем вложенные задачи в ToDo-приложении, используем MobX, React и TypeScript.</li><li><a href="https://tproger.ru/articles/sozdanie-typing-test-prilozheniya-na-react-typescript-redux-toolkit">Создание Typing Test приложения на React + TypeScript + Redux Toolkit</a> — учимся создавать приложение для проверки скорости набора текста, используем Redux Toolkit.</li><li><a href="https://tproger.ru/articles/najdi-oshibku-v-react-komponente-funkcionalnoe-karri">Найди ошибку в React-компоненте: Функциональное Карри</a> — практический кейс: ищем ошибку в React и заодно изучаем каррирование функций.</li><li><a href="https://tproger.ru/articles/windows-application-on-react">Создание приложений для Windows на React Native</a> — разбираем, как запускать и разрабатывать React Native-приложения под Windows, примеры и инструменты.</li><li><a href="https://tproger.ru/articles/recharts-optimization">Оптимизация графиков Recharts</a> — ускоряем отрисовку графиков на React с помощью Recharts, практика, кейсы и улучшения.</li><li><a href="https://tproger.ru/articles/your-first-app-in-react-native">Как разработать своё первое приложение на React Native</a> — инструкция по созданию первого мобильного приложения на React Native, от идеи до запуска.</li><li><a href="https://tproger.ru/articles/pet-proekt-na-react-kak-my-ozvuchivali-internet">Пет-проект на React. Как мы «озвучивали» интернет</a> — история реализации проекта с Web Speech API и React. Учим браузер говорить.</li></ul><p>Кстати, похожий гайд мы уже делали — про Python. Можно заценить <a href="https://tproger.ru/articles/bolwoj-gajd-po-python-ot-tproger--topovye-instrumenty-dlya-raznyh-napravlenij">здесь</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>React и RTK Query — новый лёгкий путь для redux?</title>
      <link>https://tproger.ru/articles/react-i-rtk-query-novyj-lyogkij-put-dlya-redux-</link>
      <comments>https://tproger.ru/articles/react-i-rtk-query-novyj-lyogkij-put-dlya-redux-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Alex Shulgin]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/react-i-rtk-query-novyj-lyogkij-put-dlya-redux-</guid>
      <description><![CDATA[<p>Разбираемся, как упростить запросы в react-redux с помощью redux toolkit. Пишем облегченные запросы с RTK query. Подойдет ли для всего?</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/react-i-rtk-query-novyj-lyogkij-put-dlya-redux-">React и RTK Query — новый лёгкий путь для redux?</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[DeFi]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 12 Apr 2025 09:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Прежде всего, давайте вспомним, как выглядит классический запрос и хранение данные с redux-saga и @reduxjs/toolkit.</p><p>Так выглядит стандартный шаблон запроса с манипулированием статусов и показом данных. Универсально, как швейцарский нож, и может быть полностью кастомизировано. На таком шаблоне мы вправе выполнять запросы любой сложности и контролировать все детали.</p><p>Но что если запросы такие же легкие как в примере выше? Мы просто берем данные и показываем их. Разве нам нужно каждый раз проделывать такую рутину, если можно облегчить себе жизнь?</p><p>Как раз для таких случаев есть инструмент <a href="https://redux-toolkit.js.org/rtk-query/overview">rtk-query</a> (входит в reduxjs/toolkit). Посмотрим как теперь выглядит наш запрос.</p><p>В build.query мы передаем два аргумента, 1 — тип, который вернется и 2 — параметры, которые мы принимаем в хуке в компоненте.</p><p>Далее в компоненте мы вызываем хук и передаем данные для вызова апи и вторым аргументом, параметры для самого хука. Например, свойство refetchOnMountOrArgChange перезапросит данные при unmount компоненте. В документации можно подробнее прочитать о каждом из них.</p><p>Самое главное, что в ответе хука  у нас появляются все статусы/данные/фичи. Каждый из них может быть полезен для конкретной ситуации, но все вместе они покрывает почти все кейсы.</p><p>Теперь компонент выглядит легко и изящно. Тут мы используем isFetching (отличие от isLoading в том, что он активируется при любой загрузке, а isLoading только при начальной). Можно вызывать refetch для перезапроса или проверить isError при ошибке.</p><p>Также мы можем вызывать хук с задержкой. Так называемый lazy query.</p><p>Тут мы делаем запрос из хука. Обычно это нужно, если хотим передать аргумент в хук, который появляется не сразу. Также проверяем isUninitialized, чтоб хук был проинициализирован и далее при загрузке стал false.</p><p>Плюс к этому, rtk дает возможность использовать мутации для обновления данных. Это будет выглядеть примерно так:</p><h2>Для чего не подходит rtk?</h2><p>Для сложных операций, таких как пагинация или хранение переменных между запросами. В подобных кейсах лучше использовать саги и не ставить костыли с ртк. Ведь его главная цель избавить нас от рутинных запросов.</p>]]></content:encoded>
    </item>
    <item>
      <title>Какие есть паттерны в React и для чего они нужны: часть 1</title>
      <link>https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1</link>
      <comments>https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Юсуп Изрипов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1</guid>
      <description><![CDATA[<p>В этой части Юсуп Изрипов рассказывает, что такое Container &amp; Presentational Components, Higher-Order Component (HOC) и паттерн Render Props в React и что с ними делать.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1">Какие есть паттерны в React и для чего они нужны: часть 1</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[Тимлид]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 08 Apr 2025 10:30:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>В мире React термин «паттерн» означает какой-то проверенный подход к решению задачи, а не шаблон проектирования из классической книги. За годы разработки вокруг React сформировались свои распространённые паттерны — способы организовать компоненты и логику так, чтобы код получался понятным, поддерживаемым и переиспользуемым.</p><p>Меня зовут Юсуп Изрипов, я сеньор разработчик в VK. Работаю над продуктами, которыми ежедневно пользуются миллионы человек.</p><p>В этих статьях я расскажу вам о самых популярных паттернах и приведу примеры кода. Мы рассмотрим, когда каждый из них может пригодиться, а также отметим их плюсы и минусы. Поговорим о классических приёмах вроде контейнеров и HOC, эволюции к хукам, также обязательно рассмотрим новые паттерны появившиеся в последних версиях React.</p><h2>Container + Presentational Components</h2><p>На первом месте работы ещё во время разработки на Vue мы с подачи нашего тимлида решили ввести этот паттерн. Глобально у нас были так называемые «умные» и «тупые» компоненты (или как я тактично называл их на демо — визуальные). Как вы наверняка догадались, роль Container у нас исполняли «умные» компоненты, а роль Presentational — «тупые». В чём, собственно, суть этого паттерна? Слышали выражение «разделяй и властвуй»? Паттерн Container &amp; Presentational Components (контейнерные и презентационные компоненты) ровно об этом: он разделяет логику (данные и взаимодействие с ними) и отображение (UI) на разные компоненты.</p><p>Presentational Components отвечают только за то, как что-то выглядит. Они получают данные через props и отображают их, больше ничего. Это, как правило, чистые функциональные компоненты, часто без собственного состояния (ну разве что мелкий UI-стейт типа «раскрыт ли dropdown»). Им всё равно, каким образом взять список пользователей — они просто ожидают условный props.users и отображают его в соответствии с дизайном.</p><p>Container Components, напротив, знают, что показать и откуда это взять, но не занимаются тем, как это отображается. Они содержат в себе всю логику: могут загрузить данные, подписаться на store или контекст, хранить состояние, а рендерят в презентационных компоненты, передавая им готовые данные. Контейнер может вообще не иметь собственного HTML, кроме того, что приходит от дочернего презентационного компонента. Его задача — это взаимодействие с данными.</p><p>Зачем же нужен такой подход? Во-первых, лучшая разделённость ответственности (UI отдельно, данные отдельно) упрощает понимание и поддержку приложения. Во-вторых, улучшается переиспользование: один визуальный компонент вероятно использовать с разными источниками данных через разные контейнеры. Дизайнеры могут менять внешний вид компонента в одном месте, не затрагивая бизнес-логику. И тестировать тоже проще: можно отдельно протестировать логику контейнера (без верстки) и отдельно визуальный компонент (с моком данных).</p><p>В этом примере UserList не содержит никакого стейта, не подписан на store или контекст, лишь отображает список. Ему всё равно, как и где получают пользователей — просто принимает проп users и выводит его. Контейнер UserListContainer же занимается работой с данными: делает fetch, сохраняет результат в useState, и потом рендерит UserList, прокидывая в него данные. Благодаря такому делению компонент UserList легко переиспользовать — хоть для локальных данных, хоть для данных из Redux или context — достаточно написать другой контейнер.</p><p>Конечно, не всегда нужно городить пару компонентов вместо одного. Этот паттерн полезен, когда приложение растёт: вы начинаете замечать, что пропсы идут через несколько уровней просто транзитом, или один компонент слишком перегружен логикой. Тогда вы «вытаскиваете» логику в контейнер, а UI — в презентационный компонент, и код сразу становится чище. Это не обязательное правило, а приём для рефакторинга по мере необходимости.</p><p>Стоит отметить, что с появлением React-хуков граница между логикой и отображением несколько размылась. Теперь можно выносить логику в кастомные хуки и вызывать их прямо внутри компонента, вместо того чтобы создавать отдельный контейнер-класс, как это делали до 2018 года. Тем не менее, принцип «держи логику отдельно от представления» по-прежнему полезен. Даже с хуками можно структурировать код, разделяя функциональность: написать хук useUsersData() для получения пользователей и применять его в разных компонентах (вместо дублирования запроса).</p><p>Плюсы: чёткое разделение обязанностей, возможность переиспользовать и заменять части независимо (UI-компонент можно переиспользовать с разными данными), облегчение тестирования.</p><p>Минусы: появляется больше файлов/компонентов, чем могло бы быть, что может казаться избыточным для мелких случаев. Иногда чрезмерное дробление на «глупые» и «умные» компоненты лишь усложняет структуру, если паттерн применён не к месту. Как говорится, включайте голову — не каждую кнопку нужно выделять в отдельный контейнер.</p><h2>Higher-Order Component (HOC)</h2><p>Когда я впервые услышал термин HOC, он показался мне чем-то из математики. Но на практике всё горадо прозаичнее: HOC — это всего лишь функция, которая принимает React-компонент и возвращает новый компонент, оборачивая исходный дополнительной функциональностью. Проще говоря, HOC — это «обёртка». Мы помещаем один компонент внутрь другого, чтобы на выходе получить расширенную версию переданного в HOC компонента.</p><p>Зачем это может понадобиться? Представим, у нас есть несколько разных компонентов, и всем им нужно что-то общее: например, обработка ошибок или подписка на внешние данные. Можно было бы скопировать этот код в каждый из компонентов, но куда элегантнее написать HOC один раз и применить ко всем. Классический пример — Redux-функция connect: вы пишете export default connect(mapState)(MyComponent), и ваш компонент получает пропсы из глобального стейта.</p><p>connect — как раз и есть HOC, который инъектирует данные из Redux в компонент, не требуя от вас переписывать все под Redux вручную.</p><p>Создать свой HOC тоже несложно. Супер банальный пример — сделаем HOC, который добавляет компоненту стейт счётчика:</p><p>Здесь withCounter — HOC, он возвращает новый функциональный компонент WithCounter, который внутри себя использует useState и передаёт состояние и функцию увеличения внутрь WrappedComponent. В итоге EnhancedButton — это улучшенная версия ClickButton, которая умеет считать клики, даже если исходный ClickButton об этом не знал.</p><p>Плюсы: один HOC может добавить функциональность множеству компонентов сразу — не надо копировать код везде. Логику обновляется в одном месте (внутри HOC) — и все обёрнутые компоненты получают изменения. HOC можно комбинировать: например, обернуть компонент сначала в HOC, добавляющий тему оформления, потом в HOC, добавляющий логирование, и т.д. В итоге получим компонент, обладающий сразу несколькими дополнительными возможностями.</p><p>Минусы: за такую магию мы платим усложнением структуры. Когда компонентов обёрток становится много, React-дерево раздувается, и возникает эффект «матрёшки». В DevTools вы могли видеть что-то вроде: Connect(withRouter(WithTheme(MyComponent))) — разобраться, что к чему, становится сложнее. Дебаг таких цепочек — тоже удовольствие то ещё, приходится пробираться через несколько уровней абстракций. Кроме того, HOC часто прокидывают пропсы во внутренний компонент, что чревато конфликтами имён (нужно следить, чтобы, например, prop.title от HOC не перезаписал пропс title, который вы передали самому компоненту). Ещё нюанс — HOC усложняют типизацию в TypeScript (надо правильно описывать generic для пропсов), но это выходит за рамки нашей темы.</p><p>React-разработчики со временем несколько охладели к HOC. В официальной документации прямо сказано: «компоненты высшего порядка не так часто используются в современном React-коде». Отчасти их вытеснили хуки, тем не менее, HOC никуда не делись: их продолжают применять сторонние библиотеки — тот же Redux, Relay и другие. Да, и в старом проекте вы почти наверняка встретите хотя бы пару HOC. Поэтому понимать этот паттерн стоит. Просто имейте в виду современные альтернативы и используйте HOC там, где это действительно необходимо.</p><h2>Паттерн Render Props</h2><p>Следующий паттерн я бы назвал «перевёрнутый HOC». Render Props — это подход, когда компонент сам не рендерит что-то внутри себя, а принимает функцию (часто через проп render или просто используя детей как функцию) и вызывает её, чтобы получить содержимое. То есть мы передаём компоненту инструкцию, что именно отрендерить, а он сам обеспечивает, когда и с какими данными вызвать эту инструкцию.</p><p>Представьте компонент &lt;MouseTracker&gt; для отслеживания положения курсора. Классически он может хранить x, y в своём состоянии и отрисовывать, скажем, &lt;p&gt;Mouse at (x, y)&lt;/p&gt;. Но что, если мы хотим переиспользовать эту логику уже с другим UI? Паттерн Render Props предлагает сделать компонент &lt;Mouse&gt;, который не определяет жёстко JSX внутри себя, а вызывает функцию, переданную через проп (или children функцию), передавая ей координаты. Эта функция сама решит, что рисовать. Таким образом, &lt;Mouse&gt; инкапсулирует логику (слежение за мышкой), а отображение делегирует наружу.</p><p>Пример: реализуем компонент-утилиту &lt;FilteredList items={...} filter={...}&gt;, который отображает список на основе передаваемого фильтра. Вместо того чтобы жёстко прописывать разметку элемента списка, сделаем его с render проп через children:</p><p>Здесь &lt;FilteredList&gt; знает, как отфильтровать массив (items.filter(filter)), но не знает, как отрисовать каждый элемент. Вместо этого он вызывает функцию, которую мы передали в качестве дочернего элемента (children), для каждого элемента списка. Эта функция возвращает &lt;li&gt; для каждого item. В результате логика фильтрации инкапсулируется внутри FilteredList, а конкретное отображение списка задаётся извне. Мы могли бы так же использовать этот компонент для массива объектов, отрисовывая, например, товары — достаточно передать другую children-функцию.</p><p>Паттерн Render Props здорово повышает гибкость компонентов. Мы можем переиспользовать &lt;FilteredList&gt; для списков чего угодно — чисел, пользователей, товаров — просто изменяя функцию отображения. Другой пример: компонент &lt;Mouse&gt; может предоставлять координаты курсора, а внешний код решит, просто вывести текст, нарисовать по координатам картинку или вызвать какую-то совершенно другую логику — не нужно делать несколько вариаций компонента для каждого кейса.</p><p>Плюсы: Render Props позволяет компоненту-провайдеру (в примере выше FilteredList является провайдером данных) быть максимально универсальным, а конкретную разметку делегировать наружу. Многие библиотеки воспользовались этим паттерном: например, React Router (до версии 6) позволял вместо компонента страницы передать проп render в &lt;Route&gt; — функцию, которая отрисует JSX на основе параметров маршрута. Formik предлагал компонент &lt;Formik&gt; с функцией-ребёнком для рендеринга формы. Downshift (библиотека для автокомплитов) — тоже классический пример паттерна render props.</p><p>Минусы: главное неудобство — излишний шум в JSX. Код с вложенными функциями бывает тяжело читать. В нашем простом примере всё компактно, но представьте, если у вас будет несколько уровней таких компонентов: &lt;Foo&gt;{foo =&gt; ( &lt;Bar&gt;{bar =&gt; ( ... )}&lt;/Bar&gt; )}&lt;/Foo&gt; — легко получить «оберточный ад» из стрелочных функций прямо в разметке. Это значительно затруднит отладку такого кода при возникновении каких-либо проблем. К тому же, каждый раз при рендере создаётся новая функция, что может негативно сказаться на производительности, если таких компонентов много (React конечно оптимизирует функции в пропсах через механизм сравнения, но всё же). Также возникает неявная связь: внешний код должен знать, какие аргументы ожидает функция. TypeScript конечно помогает, но при чтении кода не сразу видно, что children, например, это не просто элемент, а функция.</p><p>Как и HOC, паттерн Render Props сейчас используется реже. Многие задачи, решаемые через него, теперь элегантнее с точки зрения кода решаются хуками, в официальной документации это также отмечено. Но всё же понимать его нужно, потому что легаси-код и некоторые библиотеки всё ещё работают на нём. Если видите, что компонент принимает функцию в виде пропса (чаще всего называется render или передаваётся через детей), знайте — это он, Render Props.</p><p>В следующей части расскажу про хуки и кастомные хуки, а также про Compound Components и Серверные компоненты и Suspense.</p>]]></content:encoded>
    </item>
    <item>
      <title>Навигация в React Native: Искусство перемещения по экранам с помощью React Navigation</title>
      <link>https://tproger.ru/articles/navigaciya-v-react-native--iskusstvo-peremeshheniya-po-ekranam-s-pomoshhyu-react-navigation</link>
      <comments>https://tproger.ru/articles/navigaciya-v-react-native--iskusstvo-peremeshheniya-po-ekranam-s-pomoshhyu-react-navigation?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Сергей Дудченко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/navigaciya-v-react-native--iskusstvo-peremeshheniya-po-ekranam-s-pomoshhyu-react-navigation</guid>
      <description><![CDATA[<p>Разбираем, как организовать навигацию в React Native с помощью библиотеки React Navigation. Рассмотрим основные концепты, такие как стек и таб навигация, научимся перемещаться между экранами разных стеков и табов, а также разберём лучшие практики, хуки и подводные камни.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/navigaciya-v-react-native--iskusstvo-peremeshheniya-po-ekranam-s-pomoshhyu-react-navigation">Навигация в React Native: Искусство перемещения по экранам с помощью React Navigation</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 15 Feb 2025 09:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Салют, народ! 👋</p><p>Если вы создаете мобильное приложение на React Native, то рано или поздно столкнётесь с необходимостью организовать навигацию между экранами. Сегодня мы разберём одну из самых популярных библиотек для этого — React Navigation . Это мощный инструмент, который позволяет создавать сложные и гибкие системы навигации. Погнали!</p><h2>Что такое React Navigation?</h2><p>React Navigation — библиотека, которая помогает управлять переходами между экранами в React Native приложении и не только. Она предоставляет готовые компоненты и API для создания различных типов навигации: стек, таб, боковой навигации и даже модальных окон.</p><p>Основная идея заключается в том, что навигация представляет собой <b>дерево </b>, где каждый узел — экран или группа экранов. Это дерево можно строить рекурсивно, добавляя новые уровни навигации внутри уже существующих.</p><h3>Как это работает?</h3><p>React Navigation использует концепцию <b>навигаторов </b>(navigators). Навигатор — контейнер, который управляет группой экранов. Вот основные типы навигаторов:</p><ol><li><i>StackNavigator </i>— стек навигация, где экраны накладываются друг на друга.</li><li><i>TabNavigator </i>— таб навигация, где экраны переключаются через нижнюю или верхнюю панель.</li><li><i>DrawerNavigator </i>— выдвижная боковая панель для навигации.</li><li><i>BottomSheetNavigator </i>— модальные экраны, которые выдвигаются снизу.</li></ol><p>Каждый навигатор может содержать другие навигаторы, формируя <b>древовидную </b>структуру. Например, у тебя может быть TabNavigator, внутри которого есть StackNavigator для каждого таба. Листьями будут конечные экраны приложения.</p><h2>Структура навигации как дерево</h2><p>Структура навигации — фундамент вашего приложения. От того, насколько грамотно она организована, зависит не только удобство пользователей, но и простота поддержки кода, производительность и масштабируемость проекта.</p><p>Навигация должна быть организована логично. Например:</p><p>Здесь AppNavigator  — корневой навигатор, который содержит два дочерних навигатора: AuthNavigator и MainNavigator. В свою очередь, MainNavigator содержит HomeStack, который уже управляет экранами HomeScreen и DetailsScreen.</p><p>Такая структура позволяет:</p><ol><li><b>Логически разделить функциональность</b>: Например, экраны авторизации можно отделить от основной части приложения.</li><li><b>Управлять состоянием</b>: Каждый навигатор хранит своё состояние (текущий экран, параметры переходов и т.д.), что делает управление предсказуемым.</li><li><b>Оптимизировать рендеринг</b>: React Navigation автоматически оптимизирует рендеринг экранов, загружая только те, которые находятся в текущем фокусе.</li></ol><h2>Как строится навигация?</h2><h3>Корневой навигатор</h3><p>Корневой навигатор — первый уровень дерева. Обычно он используется для разделения логики авторизации и основного приложения. Пример:</p><p>Здесь AuthNavigator и MainNavigator — отдельные компоненты, которые содержат свои собственные навигаторы.</p><h3>Навигаторы внутри навигаторов</h3><p>Как видно из примера выше, навигаторы могут быть вложенными. Это позволяет создавать сложные системы навигации. Например, MainNavigator может быть TabNavigator, а внутри одного из табов — StackNavigator.</p><p>Здесь HomeStack — стековая навигация, которая находится внутри MainTabs.</p><h3>Логика авторизации</h3><p>Один из самых распространённых подходов — разделение навигации на две части: для неавторизованных и авторизованных пользователей. Это можно сделать с помощью глобального состояния (например, Redux или Context API).</p><p>Пример:</p><p>Такой подход позволяет легко переключаться между двумя состояниями приложения.</p><h3>Таб навигация</h3><p>Таб навигация — один из самых популярных способов организации интерфейса. Вот пример простой реализации:</p><p>Совет: используйте библиотеку react-native-vector-icons для иконок. Они делают интерфейс более привлекательным.</p><h2>Почему такая структура?</h2><h4>Разделение ответственности</h4><p>Каждый навигатор отвечает за свою часть приложения. Например:</p><ul><li>AuthNavigator управляет экранами входа и регистрации,</li><li>MainNavigator управляет основным функционалом приложения.</li></ul><p>Это упрощает поддержку кода и делает его более читаемым.</p><h4>Управление состоянием</h4><p>Каждый навигатор хранит своё состояние. Например, если пользователь находится на экране Details внутри HomeStack, то при возвращении на главный экран Home, состояние будет сохранено.</p><h4>Гибкость</h4><p>Вложенная структура позволяет комбинировать различные типы навигации. Например, вы можете использовать TabNavigator для основных экранов и StackNavigator для деталей внутри каждого таба.</p><h2>Почему не стоит использовать стандартный header?</h2><p>Стандартный header библиотеки часто выглядит слишком сырым, плохо кастомится и неочевидно работает. Лучше скрывать его и реализовывать собственный компонент для заголовков. Это даёт больше контроля над дизайном и функциональностью:</p><h3>Deeplink при помощи навигации</h3><p>Deeplink позволяет открывать конкретные экраны приложения через ссылки. Например, если пользователь кликнет на ссылку myapp://profile, он сразу попадёт на экран профиля.</p><p>Для реализации deeplink используйте метод linking в корневом навигаторе:</p><p>Более подробно про deeplink рассказывал <a href="https://tproger.ru/articles/deeplink-v-react-native--polnoe-rukovodstvo">тут</a>.</p><h3>Хуки React Navigation</h3><p>React Navigation предоставляет множество полезных хуков, например:</p><ul><li>useNavigation — получение объекта навигации,</li><li>useRoute — доступ к параметрам текущего маршрута,</li><li>useIsFocused — проверка, находится ли экран в фокусе.</li></ul><p>Пример использования:</p><p>Про хуки можно рассказывать много и долго, лучше этот момент изучить в документации. Но есть несколько советов относительно их использования:</p><ol><li><b>Не злоупотребляйте useNavigation</b>. Если вы используете useNavigation слишком часто, это может быть признаком того, что ваша архитектура требует пересмотра. Постарайтесь минимизировать его использование.</li><li><b>Избегайте лишних ререндеров</b>. Хуки, такие как useIsFocused или useFocusEffect, могут вызывать дополнительные ререндеры. Используйте React.memo или useCallback для оптимизации.</li><li><b>Очистка эффектов</b>. Не забывайте очищать эффекты в useFocusEffect, чтобы избежать утечек памяти.</li><li><b>Проверяйте типы данных</b>. При работе с useRoute всегда проверяйте, что параметры существуют, чтобы избежать ошибок.</li></ol><h3>Подводные камни и нюансы</h3><ol><li><b>Передача параметров между экранами</b>. Не забывайте очищать параметры, если они больше не нужны. Иначе это может привести к утечкам памяти.</li><li><b>Глубокое дерево навигации. </b>Слишком сложная структура навигации может затруднить отладку и поддержку кода. Старайтесь держать её максимально простой.</li><li><b>Анимации. </b>По умолчанию анимации могут быть тяжёлыми для производительности. Используйте <b>react-native-reanimated</b> для оптимизации.</li><li><b>Обновление состояния. </b>При использовании глобального состояния (например, Redux) убедитесь, что оно обновляется корректно при переходах между экранами.</li></ol><h3>Вспомогательные библиотеки</h3><p>Для улучшения работы с React Navigation полезны следующие библиотеки:</p><ol><li>react-navigation-shared-element — анимации переходов между экранами,</li><li>react-navigation-hooks — хуки для удобной работы с навигацией,</li><li>react-native-screens — оптимизация производительности экранов.</li></ol><h2>Как лучше всего построить структуру: советы</h2><h3>Минимизируйте вложенность</h3><p>Как уже упоминал ранее, слишком глубокая вложенность усложняет отладку и может привести к проблемам с производительностью. Старайтесь держать структуру максимально плоской.</p><h3>Используйте ленивую загрузку</h3><p>Ленивая загрузка экранов помогает уменьшить время загрузки приложения. Например:</p><h3>Избегайте дублирования</h3><p>Если у вас есть повторяющиеся элементы интерфейса (например, header), вынесите их в отдельные компоненты, чтобы избежать дублирования кода.</p><h3>Используйте глобальные параметры</h3><p>Для передачи данных между экранами используйте параметры маршрута (route.params). Однако избегайте передачи больших объёмов данных через параметры — лучше используйте глобальное состояние.</p><h4>Пример полной структуры</h4><p>Правильная структура навигации — ключ к созданию удобного и производительного приложения. React Navigation предоставляет мощные инструменты для организации, но важно подходить к этому процессу осознанно. Разделяйте логику, минимизируйте вложенность и следуйте лучшим практикам, чтобы ваше приложение было не только красивым, но и удобным для поддержки.</p><p>Если остались вопросы или хотите обсудить конкретные случаи — пишите в комментариях!</p>]]></content:encoded>
    </item>
    <item>
      <title>Топовые инструменты для фронтенд-разработки в 2025 году</title>
      <link>https://tproger.ru/articles/topovye-instrumenty-dlya-frontend-razrabotki-v-2025-godu</link>
      <comments>https://tproger.ru/articles/topovye-instrumenty-dlya-frontend-razrabotki-v-2025-godu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Денис Кудерин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/topovye-instrumenty-dlya-frontend-razrabotki-v-2025-godu</guid>
      <description><![CDATA[<p>Инструменты для фронтенд-разработки. Показываем актуальный инструментарий для frontend в 2025 году. Рассматриваем тренды для фронтендеров ✔ Tproger</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/topovye-instrumenty-dlya-frontend-razrabotki-v-2025-godu">Топовые инструменты для фронтенд-разработки в 2025 году</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[macOS]]></category>
      <category><![CDATA[Adobe]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Фреймворки и библиотеки]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[Дизайн интерфейсов и UX]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 03 Feb 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Специальные инструменты для разработки фронтенда существенно упрощают работу программистов, ускоряют процесс, помогают создавать привлекательные интерфейсы сайтов и решают множество других прикладных задач.</p><p>Мы собрали топ самых эффективных инструментов для фронтенда в 2025 году. Актуальная информация о фреймворках, библиотеках, средствах сборки, проверки и отладки будет полезна как начинающим, так и опытным прогерам.</p><h2>Фреймворки и библиотеки для разработки интерфейсов</h2><p>Создание фронтенда, то есть клиентской части программного продукта — неотъемлемый этап разработки сайтов и приложений. Фронтендеры работают с с HTML, CSS, JavaScript, а также используют многочисленные библиотеки с готовыми решениями. Даже новички в курсе, что без фреймворков проекты сегодня не пишутся — это долго, тяжело и непродуктивно. Ниже — наиболее эффективные фреймворки для разработки UI.</p><h3>React.js</h3><p>Созданная Джорданом Валке, ведущим программистом самой известной в мире соцсети, библиотека React остается лидером в сфере разработки инфраструктуры на JavaScript UI. На <a href="http://react.js">React.js</a> написаны такие проекты, как Skype, PayPal, Airbnb, Dropbox и многие другие.</p><p>Популярность библиотеки объясняется вполне объективными причинами:</p><ul><li>Обширное комьюнити. Только на GitHub вы найдете больше 110 тыс. публикаций с тегом «react». Библиотекой пользуются во всем мире, а тысячи профессиональных программистов постоянно обновляют инструменты и актуализируют данные. Если у разработчика появится вопрос, он обязательно найдет на него ответ в сообществе.</li><li>Технологический бэкграунд. Библиотеку создали профи из FB для себя — такие проекты делаются максимально качественно и надежно. Тот факт, что технологии доверяют крупнейшие мировые корпорации, говорит сам за себя.</li><li>Активное взаимодействие с TypeScript. Этот инструмент представляет собой продвинутую версию JS, который со временем стал слишком тяжелым в плане кода. TypeScript лишен многих недостатков Java, поэтому активно применяется в React.js. В общем, в 2025 году фреймворк точно не умрет.</li><li>Версионность. В React, в отличие от Angular или Vue, код, написанный даже несколько лет назад, без проблем запустится на новейшей версии. А если там есть неактуальный элемент, программа сама подскажет, как его исправить.</li></ul><p>В React 19 появились новые, еще более мощные компоненты, повышающие производительность и пользовательский опыт:</p><ul><li>Улучшенная поддержка Server Components, с которой более удобно работать с серверным рендерингом и загружать контент в больших проектах;</li><li>Новый API — Actions, который делает более простой и продуктивной работу с формами и интерактивными элементами;</li><li>Директивы preload и preinit обеспечивают предварительную загрузку важных ресурсов, что ускоряет работу приложений;</li><li>Новый канал релизов React Canaries позволяет протестировать некоторые опции React еще до их официального выпуска.</li></ul><p>Улучшения произошли и в экосистеме React. Упрощается работа с Redux — библиотекой, которую все чаще используют в связке с React. Инструменты для работы с таблицами и интерфейсами React Table 8 более доступны и производительные.</p><h3>Vue.js</h3><p>Еще один мощный, но при этом простой и гибкий в применении инструмент для фронтенд-разработчиков — <a href="http://vue.js">Vue.js</a>. Согласно экспертным прогнозам, он сохранит свои позиции в топе-3 фреймворков для JavaScript в 2025 году.</p><p>Относительно недавно создатели Vue радикально обновили движок и архитектуру. При этом доля TypeScript кода в репозитории приближается к 99%, что расценивается профессиональными прогерами крайне позитивно.</p><p>Команда разработчиков Vue.js, возглавляемая Эваном Ю, в свое время заявила, что фреймворк станет самым простым и удобным инструментом из всех существующих. По мнению экспертов, их слова полностью подтвердились.</p><p>В числе главных преимуществ фреймворка:</p><ul><li>Подходит для новичков. Для освоения достаточно знания HTML, CSS, JavaScript;</li><li>Небольшой вес. Размер новой заархивированной библиотеки runtime меньше 10 kb;</li><li>В пакет входит виртуальный DOM — объектная модель документа;</li><li>Активное крутое сообщество. Профессионалы помогут, если у разработчика возникнут проблемы.</li></ul><p>Количество полноценных приложений, сделанных на Vue.js, превышает 36 тысяч. Это число постоянно растет, и в 2025 году тренд обязательно продлится. В экосистему внедряются новые технологии, например, Vapor Mode, — инструмент, открывающий новые горизонты для разработки высокопроизводительного софта.</p><p>Подход, заданный Composition API, позволяет создавать компоненты через импорт функций вместо обновления опций. На выходе получаем более чистую архитектуру, что особенно важно для продуктов со сложной логикой.</p><p>Появление высокоуровнего фреймворка Nuxt 4 принесет важные улучшения в плане производительности и гибкости разработки. Поддержка Vue 3.3+ в Nuxt 4 улучшает работу с анимацией и межкомпонентные переходы. Пользовательский интерфейс становится более динамичным и плавным.</p><p>На фреймворке Vue созданы сайты Ozon, Zoom, Chess (сайт для шахматистов с посещаемостью 20 млн пользователей в месяц), Livestorm — платформа для запуска вебинаров, и многие другие ресурсы.</p><h3>Angular</h3><p>Этому мощному фреймворку доверяет Google, что уже говорит само за себя. Множество десктопных и мобильных приложений создано на <a href="https://angularjs.org/">AngularJS</a> — это крутой инструмент с минимальным риском критических ошибок. На сайте фреймворка создатели призывают разработчиков сосредоточиться на приложениях, а код Angular берет на себя.</p><p>И хотя профи считают этот фреймворк более сложным в освоении, чем Vue и React, его функциональность оправдывает все трудности учебного процесса. В 2025 году эволюция Angular продолжится: его последние версии показывают существенные изменения в подходах к реактивности ПО и управлению состоянием.</p><p>Новые решения, на которые стоит обратить внимание:</p><ul><li>К числу ключевых нововведений фреймворка относится технология Signals, которая делает взаимодействие с реактивными данными более простым. «Сигналы» актуальны в том случае, когда требуется синхронное обновление UI и оптимизация производительности приложения.</li><li>В Angular 19 компоненты Standalone стали новым стандартом. Теперь они могут использоваться без добавления в модуль. Такое решение упрощает архитектуру приложений и делает подход к коду более прямолинейным.</li><li>Внедрение зависимостей (DI, Dependency Injection) дополнено новой функцией inject() — она заменяет стандартный конструктор, что также упрощает код и делает его более гибким.</li><li>Автоматическая миграция продуктов к новому API — еще один шаг к упрощению и сокращению кода. Это напрямую отражается на производительности современных приложений, в первую очередь, на больших и логически сложных.</li></ul><p>Прежние плюсы  Angular остались неизменными — расширяемость, взаимодействие с другими фреймворками и инструментами, множество надстроек и дополнений — Auto Validate, Complete, Grid и т.д.</p><p>На фреймворке написан сайт британской газеты The Guardian, интерактивные элементы PayPal, самый посещаемый сайт мониторинга погоды Weather.com, фронтенд музыкального ресурса VEVO и многие другие продукты.</p><h3>Svelte</h3><p>В свое время фреймворк предложил принципиально новый подход к разработке пользовательских интерфейсов. Концепция создателей <a href="https://svelte.dev/">Svelte</a> — делать больше с минимальными затратами. Некоторые разработчики считают Svelte больше компилятором, чем фреймворком, что по сути соответствует действительности.</p><p>В чем основные фишки Svelte:</p><ul><li>При меньшем весе проекты более производительны — им не требуется виртуальный DOM;</li><li>Фреймворк прост в освоении, поэтому подходит начинающим разработчикам;</li><li>Опытные прогеры обращают внимание на быстроту и стабильность инструмента;</li><li>Анимации в Svelte доступны прямо из коробки — сторонние библиотеки не требуются.</li></ul><p>На текущем этапе этот фреймворк рано ставить на одну ступень с React и Vue, но его рейтинг на GitHub постоянно повышается, и в 2025 году растущий тренд точно сохранится.</p><p>Фронтенд-разработкой с фреймворком Svetle пользовались такие компании, как Spotify, Ikea, Finance.yahoo, Music.apple и многие другие.</p><h3>Solid.js</h3><p>Минималистичный фреймворк, который по производительности и быстроте превосходит все остальные фреймворки из топа. <a href="https://www.solidjs.com/">Solid</a> напоминает React, но вместо виртуального DOM использует компиляцию. Проект без проблем компилируется в JavaScript-код, что обеспечивает быстрый рендеринг страницы (трансформацию кода в картинку для пользователя).</p><p>В чем плюсы:</p><ul><li>Тонкая реактивность обеспечивает оптимизацию рендеринга в реальном времени;</li><li>Есть бесшовная интеграция со всеми актуальными библиотеками на JS;</li><li>Компоненты фреймворка — стандартные JavaScript-функции: они выполняются однократно для настройки представления, после чего автоматически обновляют интерфейс.</li></ul><p>Solid.js, по состоянию на 2025, — идеальный инструмент для одностраничных приложений и продуктов с максимальной интерактивностью (чат-ботов, мессенджеров, панелей управления).</p><p>Фреймворк используют такие ресурсы, как 1c.ru, криптовалютная биржа Ape.pro, маркетплейс Майнкрафт Waypoint Studios и другие.</p><h2>Инструменты для сборки и разработки</h2><p>По мнению большинства экспертов, в 2025 году ведущим трендом в сборке и разработке на JavaScript будет работа с универсальными мета-инструментами, совместимыми с любыми фреймворками.</p><p>Топ наиболее эффективных инструментов этого направления:</p><ul><li><a href="https://vite.dev/">Vite</a>. Локальный сервер разработки от Эвана Ю. В новой версии Vita 6 возможности существенно расширились, в том числе в плане поддержки других фреймворков, помимо Vue. Благодаря модульной архитектуре и новой системе плагинов, улучшилась кастомизация сборки (процесс внесения изменений). С Vite можно использовать несколько фреймворков в одном проекте — для сложных приложений это актуальная опция. Обновления в реальном времени происходят быстро и стабильно.</li><li><a href="https://webpack.js.org/">Webpack</a>. Сборщик модулей, который компилирует части кода в единый файл. Работает с JavaScript и TypeScript. Несмотря на появление новых инструментов сборки, в 2025 году Webpack останется обязательным инструментом, особенно для работы с масштабными проектами. Он оптимизирует размер и скорость загрузки ресурсов, эффективно выстраивает процессы разработки, в том числе HMR — горячую замену модулей. Плюс обеспечивает совместимость продуктов с различными версиями браузеров.</li><li><a href="https://parceljs.org/">Parcel</a>. Главная фишка этого инструмента в том, что его не нужно настраивать и конфигурировать — это делается автоматически. Parcel применяет воркеры — скрипты для многоядерной компиляции, и содержит кэш файловых систем для ускоренной сборки. Есть поддержка из коробки JS, CSS, HTML, поэтому соответствующие плагины не требуются. Модули, которые меняются в процессе разработки, обновляются автоматически.</li></ul><h2>Тестирование и отладка</h2><p>В 2025 году в тестировании и отладке продуктов наиболее востребованы следующие инструменты:</p><ul><li><a href="https://jestjs.io/ru/">Jest</a>. Самый популярный фреймворк для тестирования кода на JavaScript. Работает с синхронным и асинхронным кодом, интегрируется с React и Vue. В числе основных плюсов — простая настройка и удобное применение. Подходит для проверки и отладки сложных приложений.</li><li><a href="https://www.cypress.io/">Cypress</a>. Инструмент для автоматизации тестирования, который подходит новичкам. Использует тест end-to-end, покрывая весь путь пользователя, и внедряется в CI/CD — процессы непрерывной доставки и развертывания. Благодаря подробной документации подходит для новичков.</li><li><a href="https://testing-library.com/docs/react-testing-library/intro">React Testing Library</a>. Библиотека эффективных инструментов для тестирования пользовательских интерфейсов UI. RTL позволяет абстрагироваться от внутренней логики компонентов и проверяет состояние реального DOM, то есть имитирует действия пользователей, руководствуясь только данными браузера.</li><li><a href="https://storybook.js.org/">Storybook</a>. Позиционируется как максимально простое средство тестирования и разработки. Создает изолированную программную среду для ускоренного и эффективного процесса.</li></ul><h2>Инструменты для контроля версий</h2><p>Актуальные в 2025 средства для контроля созданных разработчиками версий:</p><ul><li><a href="https://git-scm.com/">Git</a>. Распределенная VCS (система управления версиями) отслеживает изменения в исходном коде и файлах в совместных проектах. Позволяет править и контролировать версии в процессе разработки. Обладает расширенными возможностями работы с репозиториями.</li><li><a href="https://github.com/gitlabhq">GitHub/GitLab</a>. Службы управления репозиториями, которые используются и для управления изменениями в опенсорс-проектах. Оба репозитория обладают полным набором инструментов для контроля и интеграций, обеспечивая эффективную совместную разработку и неограниченное сотрудничество. Обладают встроенными опциями безопасности, которые разработчики могут в любой момент активировать.</li><li><a href="https://bitbucket.org/product">Bitbucket</a>. Аналог GitHub с бесплатным доступом к контролю команды до пяти разработчиков. Подходит для написания частных проектов, обладает гибкостью, совместим со множеством других инструментов, в том числе с Jira, — средой для управления проектами.  Интеллектуальный семантический поиск JQL сканирует код по запросу пользователя.</li></ul><h2>CSS-процессоры и CSS-фреймворки</h2><p>Инструменты для первичной трансляции кода, актуальные в 2025 году:</p><ul><li><a href="http://sass-lang.com/">Sass</a>. Препроцессор упрощает написание кода, исключая одинаковые участки или заменяя ключевые элементы синтаксиса одним знаком. Sass считается самым надежным языком расширений CSS, содержит средства импорта и множество других функций.</li><li><a href="http://lesscss.org/">Less</a>. Этот препроцессор поддерживает CSS и позволяет разработчикам применять его методы для улучшения и дополнения веб-приложений. В Less встроены логические, строковые, математические функции, а также функции списков.</li><li><a href="https://getbootstrap.com/">Bootstrap</a>. Классический фреймворк для ускоренной верстки со множеством встроенных компонентов. Более 20% всех сайтов в мире создано с его помощью. Автоматически выстраивает адаптивную сетку на базе Flex-модели, создает изображения, поставляется с панелями навигации и другими компонентами.</li><li><a href="https://tailwindcss.com/">Tailwind CSS</a>. Фреймворк, который пользователи называют прогрессивной версией Bootstrap. Содержит обширный каталог утилитарных классов и инструментов для прототипирования, стилизации сайтов и приложений. Легко настраивается, работает с собственными служебными шаблонами, создает сложные адаптивные макеты, в том числе с ориентацией под мобильные устройства.</li></ul><h2>Инструменты для работы с API и серверной логикой</h2><p>Эффективная работа с API обеспечивает надежность при обработке информации в приложениях.</p><p>В 2025 году топ таких инструментов выглядит следующим образом:</p><ul><li><a href="https://graphql.org/">GraphQL</a>. Язык, описывающий взаимодействие клиента с сервером.  Рассматривается как альтернатива стандартного инструмента REST. В сравнении с последним, более удобен в применении, содержит обширный инструментарий.</li><li><a href="https://www.apollographql.com/docs/react">Apollo Client</a>. Язык запросов, созданный разработчиками FB. Интегрируется с GraphQL и React для управления состоянием приложения. Предоставляет эффективные инструменты в виде поддержки кэширования, управления состоянием и исправления ошибок. Интегрируется с библиотекой Redux и другими фреймворками.</li><li><a href="https://axios-http.com/ru/docs/intro">Axios</a>. JS-библиотека для работы с HTTP-запросами, а по сути — клиент для работы в браузере и с платформой Node.js. Помогает настроить взаимодействие между frontend и backend.</li></ul><h2>UI и UX-дизайн</h2><p>Интерфейсу и комфорту пользователя современные сайты и приложения уделяют максимум внимания. На кривые и неудобные ресурсы посетители просто не приходят — зачем, если есть лаконичные и функциональные продукты, отвечающие актуальным трендам цифрового дизайна.</p><p>Эти инструменты сохраняют свою актуальность в 2025 году:</p><ul><li><a href="https://www.figma.com/">Figma</a>. Графический редактор, который остается самым популярным инструментом для проектирования интерфейсов, прототипирования и тестирования. Также это среда взаимодействия команды разработчиков, доступная непосредственно в браузере. Содержит удобные инструменты, может работать в режиме многозадачности.</li><li><a href="https://helpx.adobe.com/ru/xd/get-started.html">Adobe XD</a>. ПО для работы с интерфейсами, анимированием, прототипами сайтов, сервисов и приложений от лидера индустрии. Основной инструмент UX-дизайнеров, желающих создавать юзабельные и современные макеты и делиться результатами работы с командой. Содержит многочисленные инструменты для рисования, создания 3D-эффектов, добавления интерактивных функций и анимации.</li><li><a href="https://www.sketch.com/">Sketch</a>. Выбор многих профессионалов индустрии UI/UX дизайна. Упрощает разработку интерфейсов благодаря множеству встроенных полезных функций — артбордов, редакторов, поддержкой командной работы в режиме онлайн. Работает только с macOS.</li></ul><p>При выборе фреймворков и инструментов фронтам стоит руководствоваться удобством и простотой использования, соответствием собственному профессиональному уровню, языковой поддержкой, ценой, а также спецификой создаваемого продукта.</p><p>Вспомогательные инструменты ускоряют и упрощают разработку, снижают риск ошибок и работают на результат — запуск привлекательных, быстрых и функциональных веб-продуктов и приложений.</p><p><i>Рассказывайте в комментариях, какими фреймворками и библиотеками пользуетесь вы. А если хотите найти большей полезной информации про фронтенд — вам </i><a href="https://t.me/+BDTRdPNEOY00ZjE6">сюда</a><i>.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Как я создала приложение, которое решает, что мне есть</title>
      <link>https://tproger.ru/articles/kak-ya-sozdala-prilozhenie--kotoroe-rewaet--chto-mne-est</link>
      <comments>https://tproger.ru/articles/kak-ya-sozdala-prilozhenie--kotoroe-rewaet--chto-mne-est?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Ekaterina Davidova]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-ya-sozdala-prilozhenie--kotoroe-rewaet--chto-mne-est</guid>
      <description><![CDATA[<p>Работать на удалёнке прекрасно, за исключением одного — всё время нужно что-то готовить. А для этого — придумать, что бы такого вкусного тебе хотелось съесть сегодня. 
Меня зовут Лена Райан, я фронтенд-разработчик в Точка Навыки. Недавно закончила свой новый пет-проект — приложение, которое анализирует, какие продукты уже есть дома, и даёт подсказки, что можно из них сделать. В этой статье рассказываю, с какими сложностями пришлось столкнуться, и что в итоге получилось. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-ya-sozdala-prilozhenie--kotoroe-rewaet--chto-mne-est">Как я создала приложение, которое решает, что мне есть</a>»</p>]]></description>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Веб-дизайн]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[Пет-проект]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 31 Jan 2025 13:58:45 GMT</pubDate>
      <content:encoded><![CDATA[<h2>С чего всё началось</h2><p>Впервые идея создать приложение для подбора меню появилась в начале 2024 года. На тот момент в моей жизни было две главные проблемы:</p><ul><li>Я работала в Учи.ру, где мы писали браузерную игру на чистом JS, поэтому я очень скучала по реакту.</li><li>Мне надоело придумывать, что готовить. Из-за этого мы с мужем перешли на готовые рационы. Но, во-первых, питаться только доставками дорого. А во-вторых, всё равно приходилось мыть посуду, потому что мы сознательные разработчики, которые сортируют мусор и сдают пластик в переработку.</li></ul><p>В какой-то момент я поняла, что пришло время перемен, и придумала, как разом решить все мои проблемы — нужно создать приложение для подбора меню.</p><h2>Подготовка</h2><p>Первое, что я сделала — набросала схему в FigJam. Здесь отметила, какие функции обязательно должны быть в MVP, а какие войдут в следующие версии проекта (v1.5, v2 и даже v3).</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-01-29/efbba717-1c8d-4dc9-8cf6-a787991511d0.png" alt="" /><figcaption>План развития проекта</figcaption></figure><p>После этого нарисовала первый прототип приложения. Потом поняла, что совсем не шарю в графике, и обратилась к подруге — веб-дизайнеру. Вот такое «до/после» у нас получилось.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-01-29/aebfabde-35cc-4165-a89d-983c61fb2569.jpeg" alt="" /><figcaption>Мой дизайн приложения</figcaption></figure><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-01-29/1025b83c-9801-4040-b426-bffabb92ec9a.jpeg" alt="" /><figcaption>Итоговый вариант от веб-дизайнера</figcaption></figure><p>Чтобы ничего не забыть, все задачи отмечала в Notion, а когда он закрылся, перешла в AnyType. Кому-то он нравится больше, но лично мне не хватило возможности переносить таски из одного столбика в другой (или я просто не поняла, как это делать). Поэтому в скором времени планирую переезд в Obsidian.</p><h2>Кое-что по технарю</h2><p>Для разработки я выбрала эти инструменты:</p><ul><li>Библиотека: ReactJS. Думаю, пояснения будут излишни.</li><li>Стор: Redux Toolkit. Много раз видела его в вакансиях, но ни разу с ним не работала, поэтому было интересно пощупать на практике.</li><li>Базы данных: Supabase — аналог Firebase. Тоже взяла впервые.</li><li>Сборка: Vite, потому что он быстрый и классный.</li><li>Деплой: Vercel. В отличие от GitHub Pages может делать всё за одно нажатие кнопки.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-01-29/29aa899d-ea9e-4b00-a701-be8250b104af.jpeg" alt="" /><figcaption>Инструменты, которые я использовала в работе</figcaption></figure><p>Ещё хотела использовать UI-библиотеку, но так как приложение небольшое, то решила не тащить лишние килобайты и сверстать всё сама.</p><p>Из того, что приятно удивило в процессе работы — я перестала искать уроки на YouTube. Всю информацию по новым инструментам брала в официальных документациях: сейчас они стали подробными и понятными.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-01-29/e77d939e-abed-46d5-bad7-3eac61d3241f.jpeg" alt="" /><figcaption>Всю информацию по новым инструментам брала в официальной документации</figcaption></figure><h2>Как работают алгоритмы</h2><p>Основа приложения — это три главных экрана:</p><ul><li>рецепты,</li><li>холодильник,</li><li>список покупок.</li></ul><p>Работает это просто: я заполняю раздел «Холодильник» и отмечаю, какие продукты уже есть дома, а приложение подбирает подходящие рецепты из базы данных.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-01-29/c4433f85-2581-41de-8ea1-acf6f4284d21.jpeg" alt="" /><figcaption>Приложение подбирает рецепты с учётом имеющихся продуктов</figcaption></figure><p>Например, сейчас в базе есть рецепты шакшуки, авокадо-тоста или гранолы с фруктами. При этом для шакшуки у меня есть 67% ингредиентов, для тоста — ровно половина, а для гранолы — только 33%. Поэтому идём по пути наименьшего сопротивления и выбираем на завтрак яйца. А недостающий перец автоматически отправляется в список покупок.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-01-29/90770b8d-5dd5-4079-895b-ab1e10310533.png" alt="" /><figcaption>Как работают алгоритмы приложения</figcaption></figure><p>Конечно, высчитывать проценты не обязательно, тем более, все равно нужно идти в магазин. Но, во-первых, не хочется тащить до дома гигантский пакет, а, во-вторых, нужно успеть использовать уже имеющиеся продукты до истечения срока годности.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-01-29/995524ee-fafa-41cf-aabd-750991502823.jpeg" alt="" /><figcaption>Список продуктов и покупок</figcaption></figure><p>Список покупок можно заполнять и вручную. Если продукт, который вы хотите добавить, там уже есть, то высветится предупреждение. А вот рецепты пока пополняются только через базу данных — в будущем хочу это исправить.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-01-29/47f278f4-7a89-4af2-9220-764e289460f9.jpeg" alt="" /><figcaption>Пополнять базу данных пока можно только вручную через Supabase</figcaption></figure><h2>Немного проблем</h2><p>В процессе работы было несколько трудностей:</p><ul><li>Нехватка времени. Я начала делать приложение на новогодних каникулах, а потом они кончились, и свободного времени не осталось.</li><li>Лень. Если честно, заполнять базу данных рецептами довольно муторно, поэтому со временем мы снова перешли на доставку готовых рационов.</li><li>Странности с Supabase. На бесплатном тарифе база данных замораживается, если не пользоваться ей неделю. Потом можно опять активировать, но я думаю перейти на Nitra, чтобы ни от кого не зависеть.</li><li>Уход от плана. В какой-то момент я настолько вошла во вкус, что забыла про свои схемы и черновики. В итоге актуальная версия приложения очень отличается от той, что была запланирована.</li></ul><h2>Планы на будущее</h2><p>Сейчас приложение на этапе MVP, но есть много идей, куда и как его можно развивать. Вот что точно хочу сделать в будущем:</p><ul><li>Общий список покупок. Чтобы там были не только продукты, но и другие вещи. Думаю, будет логично хранить всё в одном месте.</li><li>Генерация меню на несколько дней. Это упростит процесс покупок, и не придётся лишний раз ходить в магазин.</li><li>Внесение блюда в базу данных через интерфейс. Надеюсь, это решит мою проблему с ленью, и я найду силы и время добавить в приложение все рецепты.</li><li>Тёмная тема. Она уже готова, нужно просто подключить.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-01-29/36d3f802-3a81-4fe3-af8a-1538cdd8d063.jpeg" alt="" /><figcaption>Так будет выглядеть тёмная тема приложения</figcaption></figure><p>Ещё думаю над тем, чтобы вывести приложение в паблик и дать возможность всем желающим подключить свою базу данных. С open-source всё сложнее — пока не настолько доверяю людям, чтобы дать им возможность решать, что будет у меня на завтрак 🙂</p><h2>Зачем делать пет-проект</h2><p>Пет-проект — это классный способ прокачать свои навыки. Если у вас тоже есть своя задумка, рекомендую не откладывать её в долгий ящик. С помощью пет-проекта вы можете:</p><ul><li>Решить свою актуальную проблему, как это сделала я. С одной стороны — сняла головную боль с придумыванием блюд, с другой — вернулась к любимому реакту.</li><li>Поработать с новыми технологиями, которые хочется пощупать, но нет возможности в основном проекте.</li><li>Попробовать себя в новых ролях. Если на работе вы только разработчик, то здесь одновременно аналитик, архитектор, проджект-менеджер, тестировщик и дизайнер;</li><li>Пополнить резюме. Новый опыт и технологии точно пригодятся на собеседованиях.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Какой state management вы используете в больших React-приложениях?</title>
      <link>https://tproger.ru/articles/upravlenie-sostoyaniem-v-bolwih-react-prilozheniyah-250768</link>
      <comments>https://tproger.ru/articles/upravlenie-sostoyaniem-v-bolwih-react-prilozheniyah-250768?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Денис Кудерин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/upravlenie-sostoyaniem-v-bolwih-react-prilozheniyah-250768</guid>
      <description><![CDATA[<p>Всем привет! Какой стэйт менеджмент вы используете в больших React-приложениях? </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/upravlenie-sostoyaniem-v-bolwih-react-prilozheniyah-250768">Какой state management вы используете в больших React-приложениях?</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Флудильня]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 07 Jun 2024 09:20:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Мы пробовали Redux, но он оказался громоздким. Рассматриваем Context API, но сталкиваемся с prop drilling. В одном из проектов контекст API начал сильно тормозить при большом количестве компонентов.</p><p>Как решаете проблемы с производительностью и масштабируемостью? Было бы здорово услышать реальные кейсы.</p>]]></content:encoded>
    </item>
    <item>
      <title>Упрощенный Redux</title>
      <link>https://tproger.ru/articles/uproshhennyj-redux</link>
      <comments>https://tproger.ru/articles/uproshhennyj-redux?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Sultan]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/uproshhennyj-redux</guid>
      <description><![CDATA[<p>Рассмотрели абстракции, линзы и каррированные функции в Redux, слегка коснувшись комбинаторного программирования.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/uproshhennyj-redux">Упрощенный Redux</a>»</p>]]></description>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 09 Nov 2023 11:21:09 GMT</pubDate>
      <content:encoded><![CDATA[<p>В первой части статьи <a href="https://tproger.ru/articles/deklarativnyj-javascript">Декларативный JavaScript </a> мы рассмотрели конструкцию switch/case и представили её в виде функций высшего порядка. В этой части мы продолжим развивать эту идею и применим её в создании редьюсеров <a href="https://redux.js.org/">Redux</a>.</p><p>Несмотря на то, что популярность библиотеки Redux уже стихла, она идеально подходит для разбора примеров по двум причинам: во-первых, многие знакомы с этой библиотекой, а во-вторых, код, который создаётся вокруг этой библиотеки, можно упростить и сделать читабельнее.</p><h2>Абстракции</h2><p>Возможно, вы замечали, что если указать собаке пальцем на предмет, она будет смотреть на кончик пальца, а не на сам предмет. Для собаки сложно представить воображаемую линию между кончиком пальца и предметом. В её сознании такая абстракция не существует. К счастью, у нас есть такой навык и он нам позволяет упрощать сложные вещи и представлять их в простой форме.</p><p>Например, на диаграмме выше нарисован квадратик, который символизирует приложение, а бочка, присоединенная к квадратику, означает базу данных. Такие диаграммы помогают нам понять сложные архитектурные решения, не углубляясь в детали реализации. Эту же методику можно применить и в программировании, выделяя основной код от второстепенного, пряча их за функциями.</p><p>Создание редьюсера подобно оглавлению книги: оно позволяет быстро найти необходимую функцию по типу экшена. Функции, такие как signIn и signOut, иллюстрируют преобразования состояния объекта: они принимают на вход полезную нагрузку и текущее состояние объекта. Второстепенный код, включающий проверку типов экшена, вызов нужной функции и прочее, скрыты за функциями createReducer и on.</p><p>Для удобства мы добавим вспомогательные функции useAction и useStore:</p><h2>Линзы</h2><p>В функциональном программировании, линзы – это абстракции, которые позволяют работать с вложенными структурами данных, такими как объекты или массивы. Проще говоря это иммутабельные сеттеры и геттеры. Эти функции называются линзами, потому что они позволяют сфокусироваться на значении определенной ветки объекта:</p><figure><img src="https://media.tproger.ru/user-uploads/90257/2023-11-09/66762715-55f8-402b-bdc6-59e8ae34a2d5.png" alt="" /></figure><p>Для начало давайте взглянем как обновляется значение объекта без линз:</p><p>:</p><p>Ниже представлена реализация линз. Обратите внимание, что все эти функции уже существуют в библиотеке <a href="https://ramdajs.com/">Ramda</a>:</p><h2>Каррированные функции</h2><p>Как уже мы выяснили, каррированные функции очень удобны при композиции функций или передаче переменных в контекст функции из разных мест. Но они выглядят неуклюже, когда требуется передать все параметры сразу. Например, каррированая версия set будет выглядеть так:</p><p>Можно написать функцию curry, которая позволит преобразовать любую функцию в каррированную и позволит вызывать её с разным количеством параметров:</p><p>Ниже приведена реализация и пример ее использования.</p><p>Я часто замечаю, что многие разработчики по какой-то причине оборачивают callback функции в анонимные, только чтобы передать параметр.</p><p>Вместо этого просто нужно передать функцию в качестве параметра. Однако стоит помнить, что функция then ожидает колбэк-функцию с двумя параметрами. Поэтому прямая передача будет безопасной только если setUsers ожидает только один параметр.</p><p>Это напоминает мне сокращение дробей или уравнения из школьной алгебры.</p><figure><img src="https://media.tproger.ru/user-uploads/90257/2023-11-09/f596d457-2ac8-4f3d-9b29-34dc1b10bbd0.png" alt="" /></figure><p>Предлагаю сократить функцию updateCity:</p><p>Можно сразу вставить функцию в редюсер без объявления переменной:</p><p>Но самое главное, теперь функцию set можно включить в композицию и выполнять несколько обновлений:</p><p>Такой стиль программирования называется <a href="https://ru.wikipedia.org/wiki/%D0%91%D0%B5%D1%81%D1%82%D0%BE%D1%87%D0%B5%D1%87%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5">комбинаторным</a> (point free) и широко используется в функциональном программировании.</p><figure><img src="https://media.tproger.ru/user-uploads/90257/2023-11-09/99319d30-4a19-403d-b3ee-e50edd10894c.gif" alt="" /></figure><p>Эта статья получилась немного длиннее, чем планировалось, но я надеюсь, что вы узнали что-то новое для себя. В следующей статье мы представим конструкцию try/catch в виде функции. И, как обычно, небольшой тизер к следующему посту:</p>]]></content:encoded>
    </item>
    <item>
      <title>Создание Typing Test приложения на React + TypeScript + Redux Toolkit</title>
      <link>https://tproger.ru/articles/sozdanie-typing-test-prilozheniya-na-react-typescript-redux-toolkit</link>
      <comments>https://tproger.ru/articles/sozdanie-typing-test-prilozheniya-na-react-typescript-redux-toolkit?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Viktor]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/sozdanie-typing-test-prilozheniya-na-react-typescript-redux-toolkit</guid>
      <description><![CDATA[<p>Рассказал о создании приложения для проверки скорости и точности печати (Typing Test App) на React + TypeScript и Redux Toolkit.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/sozdanie-typing-test-prilozheniya-na-react-typescript-redux-toolkit">Создание Typing Test приложения на React + TypeScript + Redux Toolkit</a>»</p>]]></description>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 31 Jul 2023 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Всем доброго времени суток! В данной статье я хочу рассказать о создании приложения для проверки скорости и точности печати (Typing Test App). Приложение будем создавать на React + TypeScript, для работы с состоянием приложения используем Redux Toolkit.</p><p>В статье я не буду описывать создание стилей, а с полным кодом приложения вы можете ознакомиться в <a href="https://github.com/Umlen/typing-test">репозитории проекта</a>.</p><h2>Задачи</h2><ol><li>У пользователей должна быть возможность выбирать количество предложений</li><li>Текст необходимо получать из внешнего API;</li><li>Применение соответствующих стилей для правильного и неправильного символа;</li><li>Подсветка текущего символа;</li><li>Приложение должно вычислять и отображать скорость и точность печати текста пользователем;</li><li>Пользователи должны иметь возможность перезапустить текущий тест.</li></ol><h2>Настройка проекта</h2><p>Создадим React проект с TypeScript шаблоном, выполнив в терминале следующую команду: npx create-react-app typing-test-app --template typescript.</p><p>После завершения установки, перейдем в директорию проекта, установим axios (npm install axios) и Redux Toolkit (npm install @reduxjs/toolkit react-redux).</p><p>Удалим все файлы, которые нам не пригодятся (оставим index.tsx, App.tsx, index.css, react-app-env.d.ts). Создадим папки api, assets, components, helpers, redux, types и style. Файл index.css переместим в папку style.</p><p>Отредактируем оставшиеся файлы.</p><p><i>Файл index.tsx:</i></p><p><i>Файл App.tsx:</i></p><p>Приложение мы пишем на TypeScript, поэтому для функциональных компонентов желательно указывать тип FunctionComponent, который мы импортируем из React.</p><h2>Header и Footer</h2><p>В папке components создаем папку ui, в которой создадим два компонента Header и Footer. В Header, импортируем логотип и стили. Возвращать будем </p> с логотипом и названием приложения.<p></p><p>В Footer импортируем стили и возвращаем </p> с одним абзацем.<p></p><p>Импортируем созданные компоненты в App.tsx.</p><h2>Создание базовых компонентов</h2><p>В папке components создадим компонент Test, пока что этот компонент будет отображать только слово ‘Test’.</p><p>В папке ui создадим компонент Button. Импортируем встроенный тип ComponentPropsWithoutRef, с помощью него мы сможем получить все атрибуты, которые есть у элемента  и использовать эти атрибуты внутри нашего компонента в качестве props.</p><p>Там же создаем компонент ModalWindow. В этот компонент мы будем передавать текст для заголовка окна и необходимые элементы в качестве children.</p><h2>НастройкаRedux Toolkit</h2><p>Перед тем как использовать созданные компоненты в проекте, мы создадим состояние для теста. Для этого воспользуемся Redux Toolkit.</p><p>В папке redux, создадим папку store, в которой создадим файл testSlice. Опишем тип для состояния теста. Для isTestStarted и isTestFinished, мы зададим тип boolean, а sentences будет string. Начальным состоянием isTestStarted и isTestFinished установим false, а sentences будет строкой с цифрой 4.</p><p>Далее, с помощью метода createSlice создадим слайс для нашего приложения. У нас будет четыре редюсера, по одному для работы с каждым отдельным состоянием и один общий, для сброса состояния теста к исходным значениям. Для типизации action.payload воспользуемся встроенным типом PayloadAction, которому в качестве дженерика будем передавать необходимый тип.</p><p>Далее, в папке store создаем одноименный файл и в нем опишем store нашего приложения. Редюсер у нас пока один, импортируем его из файла testSlice.</p><p>Экспортируем созданный store, и так как мы используем TypeScript, необходимо создать и экспортировать дополнительные типы. Они понадобятся для работы с Redux хуками. Для RootState воспользуемся встроенной TypeScript утилитой – ReturnType, которая будет принимать определение типа метода getState, а возвращать тип возвращаемого getState значения. Для определения типа AppDispatch воспользуемся оператором typeof.</p><p>В папке redux создадим файл hooks, импортируем в него созданные типы. И создаем два кастомных хука: useAppDispatch и useAppSelector. Создание этих хуков подробно описано в <a href="https://redux-toolkit.js.org/tutorials/typescript#define-typed-hooks">документации Redux Toolkit</a>, это просто типизированные версии встроенных Redux хуков useDispatch и useSelector.</p><p>В файле index.tsx. Обернем компонент App в Provider. В Provider передадим созданный store.</p><p>Откроем файл App, импортируем в него созданные хуки, функции setIsTestStarted и setSentences, и компоненты Button и ModalWindow.</p><p>Создадим функцию testStateToggler, внутри которой будем изменять состояние isTestStarted на true, тем самым запуская тест.</p><p>В блок </p> добавим рендер по условию, если isTestStarted – true, то отрисовываем Test, если false, то отрисовываем ModalWindow. В ModalWindow передаем Button с функцией testStateToggler для события onClick.<p></p><p>Теперь изначально будет отображаться компонент ModalWindow, а при нажатии на кнопку Start, появится компонент Test.</p><h2>Работа с текстом</h2><p>Для получения текста используем <a href="https://baconipsum.com/json-api/">https://baconipsum.com/json-api/</a>. В папке api создадим файл getText, который будет содержать асинхронную функцию. Внутри этой функции мы с помощью axios отправляем GET запрос на сервер и возвращаем ответ, в нашем случае в ответ на запрос мы ожидаем получить строку с текстом. В качестве аргумента функция getText будет принимать количество запрашиваемых предложений. Остальные параметры запроса берем из документации baconipsum</p><p>Теперь опишем тип для массива символов. В папке types создадим одноименный файл. Массив будет состоять из объектов с двумя свойствами: сам символ и css – класс.</p><p>Для работы с текстом создадим отдельный слайс, перейдем в папку redux/store и создадим там файл textSlice. Импортируем в него TextType и функцию getText. Для выполнения асинхронного запроса нам понадобится метод createAsyncThunk, импортируем его из redux toolkit.</p><p>Начнем с описания типа, здесь нам понадобится сам массив символов – text, состояние загрузки текста – isLoading, возможные ошибки загрузки – error, также нам нужно следить за индексом текущего символа – currentCharIndex, считать количество ошибок – mistakes и общее количество нажатий – pressingCount.</p><p>Далее, используя метод createAsyncThunk, создаем функцию fetchText. Для типизации createAsyncThunk используем дженерик, в который мы передадим три параметра. Первым параметром будет string, т.к. мы ожидаем, что функция вернет нам строку, вторым параметром также будет string, т.к. именно строку мы будем передавать функции колбэку и третьим параметром укажем rejectValue, он нам понадобится для обработки ошибок.</p><p>В сам метод createAsyncThunk мы передаем имя для action и асинхронную функцию для получения текста. Внутри этой асинхронной функции воспользуемся ранее созданной функцией getText, в которую передадим количество запрашиваемых предложений. В случае успеха будем возвращать response.data, в случае ошибки вернем встроенную функцию rejectWithValue с сообщением об ошибке.</p><p>В объекте reducers опишем редюсеры для работы с состоянием текста. setText для изменения массива с текстом, setCurrentCharIndex и setMistakes для изменения индекса текущего символа и количества ошибок соответственно, increasePressingCount для подсчета количества нажатий и resetTextState для сброса состояния к начальным значениям.</p><p>В extraReducers будем обрабатывать action для функции fetchText. Во время загрузки изменяем  состояние isLoading на true и обнулять error. При успешном получении данных будем разбивать полученную строку на символы и формировать массив объектов, который сохраним в text. Для символа с индексом 0 сразу будем устанавливать класс ‘current-char’. В случае ошибки будем присваивать в error сообщение об ошибке.</p><p>Импортируем textSlice в наш store.</p><p>Для стилизации символов нам необходимо создать две функции. Первая будет стилизовать текущий символ, а вторая будет сверять нажатую клавишу с текущим символом и применять необходимый стиль, правильного или неправильного символа.</p><p>В папке helpers создадим файл charTransform. Начнем с функции getCurrentChar, на вход она будет принимать массив типа TextType и индекс текущего элемента, а возвращать новый массив типа TextType. Внутри самой функции будем перебирать входной массив и сравнивать индекс элемента массива с индексом текущего элемента, если индексы равны, то возвращаем элемент с классом current-char.</p><p>Теперь создадим функцию compareChars, на вход она будет принимать массив типа TextType, индекс текущего элемента, количество ошибок и нажатую клавишу, возвращать будет новый массив типа TextType, новый индекс текущего элемента и новое количество ошибок. Внутри функции делаем примерно тоже самое, проходим по массиву, сравниваем индексы и проверяем, правильно ли нажата клавиша.</p><p>Далее, в папке components создадим компонент Text. Импортируем в него функции getCurrentChar и compareChars. Из textSlice импортируем функции fetchText, setText, setCurrentCharIndex, increasePressingCount и setMistakes.</p><p>Для получения текста, внутри хука useEffect будем вызывать функцию fetchText. В которую будем передавать переменную sentences.</p><p>Добавим еще один useEffect, в котором при изменении индекса текущего символа, будем вызывать функцию getCurrentChar и изменять текст.</p><p>Для обработки нажатия клавиш также используем useEffect. Внутри будем сравнивать currentCharIndex с длиной текста и если currentCharIndex меньше, то используя конструкцию Function Expression, создаем функцию обработчик. В этой функции будем вызывать функцию compareChars, и обновлять текст, индекс текущего символа, количество ошибок и количество нажатий.</p><p>В этом компоненте мы используем условный рендеринг. Если есть ошибка, будем выводить на экран значение переменной error, если идет загрузка, будем отображать параграф с текстом «Loading text…», а когда текст загружен, будем выводить его на экран.</p><p>Импортируем созданный компонент Text в компонент Test.</p><h2>Подсчет скорости и точности печати</h2><p>Для подсчета скорости печати нам потребуется таймер. В папке redux/store создадим файл timerSlice. Начальным состоянием будет выключенный таймер и количество секунд равное 0. Также будет три редюсера, для изменения состояния таймера, увеличения секунд на 1 и обнуления секунд.</p><p>Импортируем timerSlice в наш store.</p><p>Теперь нам необходимо добавить включение и выключение таймера в компонент Text. Перед созданием обработчика нажатия клавиш, будем проверять количество нажатий и при первом нажатии запускать таймер. Внутри функции keyPressHandler будем проверять новое значение индекса текущего символа, и если оно равно длине текста, то будем выключать таймер и устанавливать состояние isTestFinished в значение true.</p><p>Далее создадим функции подсчета скорости и точности печати. Для этого в папке helpers создадим файл statsCounting, и в нем создадим две эти функции.</p><p>Функция accuracyCounting будет принимать количество ошибок и общее количество нажатий, а возвращать процент правильно нажатых клавиш. Так как общее количество нажатий может быть равно нулю, необходимо это проверить.</p><p>Функция speedCounting принимает количество правильных символов и количество секунд. Возвращает скорость печати (количество слов в минуту). Для вычисления скорости необходимо перевести секунды в минуты, а количество правильных символов в количество слов (обычно подобные приложения берут среднюю длину слов равную пяти символам). Секунды могут быть равны нулю, поэтому их также необходимо проверить.</p><p>Теперь в папке components создадим компонент Stats, который будем использовать для отображения статистики. Импортируем в него функции speedCounting, accuracyCounting и increaseSeconds. В качестве props этот компонент будет принимать только необязательных children.</p><p>Создадим состояние для скорости и точности, а ошибки, нажатия, секунды и состояние таймера будем брать из глобального состояния. Воспользуемся хуком useEffect и при изменении количества ошибок, нажатий или секунд, будем вызывать функции speedCounting и accuracyCounting и устанавливать результаты вызова этих функции в соответствующее состояние.</p><p>Добавим еще один useEffect, в котором будем проверять, включен ли таймер, и если включен, то будем увеличивать количество секунд.</p><p>Импортируем созданный компонент в компонент Test.</p><h2>Завершение теста</h2><p>В файле charTransform создадим еще одну функцию, она будет приводить массив с текстом в изначальное состояние. Принимать и возвращать она будет массив типа TextType. Внутри функции будем перебирать массив и устанавливать пустую строку в значение поля class вложенных объектов. Для объекта под индексом 0 class установим ‘current-char’.</p><p>В компоненте Test создадим две функции, для перезапуска и для начала нового теста. Добавим условный рендеринг, если isTestFinished – true, то будем отображать компонент ModalWindow со статистикой и двумя кнопками, Restart и New Test. Также в первый компонент Stats добавим кнопку перезапуска теста. Так как после нажатия кнопки, фокус остается на ней, нужно его принудительно снять, если этого не сделать, то при нажатии пробела во время печати кнопка будет срабатывать.</p><h2>Выбор количества предложений</h2><p>Последнее, что осталось сделать – это добавить возможность выбирать количество предложений. Для этого в папке components/ui создадим компонент Select.</p><p>Тут нам также, как и в компоненте Button потребуется тип ComponentPropsWithoutRef, чтобы мы могли получить все атрибуты элемента select. В качестве props компонент будет принимать значение по умолчанию и массив значений. Возвращать будет один элемент select.</p><p>Теперь перейдем в App и добавим созданный компонент Select в отрисовку ModalWindow. В качестве defaultValue будем передавать состояние sentences, для options создадим небольшой массив объектов с двумя полями value и name, а при событии onChange будем изменять значение sentences.</p><p>Приложение работает и полностью готово.</p><h2>Заключение</h2><p><a href="https://github.com/Umlen/typing-test">Репозиторий проекта</a> и <a href="https://stellular-rolypoly-3ac804.netlify.app/">Live</a>. Буду рад вашим комментариям и советам. С радостью отвечу на любые вопросы!</p>]]></content:encoded>
    </item>
    <item>
      <title>5 архитектурных ошибок, которые мы совершаем на старте проектов</title>
      <link>https://tproger.ru/articles/5-arhitekturnyh-owibok--kotorye-my-soverwaem-pri-starte-proektov</link>
      <comments>https://tproger.ru/articles/5-arhitekturnyh-owibok--kotorye-my-soverwaem-pri-starte-proektov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/5-arhitekturnyh-owibok--kotorye-my-soverwaem-pri-starte-proektov</guid>
      <description><![CDATA[<p>Какие архитектурные ошибки чаще всего совершают разработчики при запуске проектов и как их избежать? Разбираем пять критичных промахов, которые мешают продукту масштабироваться и усложняют поддержку.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/5-arhitekturnyh-owibok--kotorye-my-soverwaem-pri-starte-proektov">5 архитектурных ошибок, которые мы совершаем на старте проектов</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Django]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[Маркетинг]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[микро]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 14 Jul 2023 12:10:24 GMT</pubDate>
      <content:encoded><![CDATA[<p>Можно запустить MVP за пару недель, но потом годами разгребать архитектурные долги. Ошибки на старте проекта легко не заметить: код вроде бы работает, фичи выкатываются, пользователи приходят. Но когда продукт растёт, каждая непродуманная деталь в архитектуре оборачивается багами. Разбираем пять ключевых ошибок, которые разработчики совершают чаще всего, и объясняем, как их избежать.</p><h2>Ошибка 1. Нет четких границ между слоями приложения</h2><p>На старте проекта код часто пишется «в лоб»: контроллеры ходят в базу напрямую, внутри функций появляются бизнес-правила и расчёты, а валидация размазывается по коду. Это кажется удобным, пока приложение небольшое. Но при росте команды и количества фич изменения начинают ломать соседние части кода, баги растут, а скорость разработки падает.</p><p>Можно выделить три базовых уровня:</p><ul><li>Контроллеры (или хэндлеры) принимают запросы, проводят базовую валидацию (например, через pydantic-схемы) и передают данные дальше.</li><li>Слой бизнес-логики реализует правила работы приложения: транзакции, расчёты скидок, проверку лимитов, работу с несколькими сервисами.</li><li>Слой доступа к данным отвечает за запросы к базе и возвращает данные в удобной для бизнес-логики форме.</li></ul><p>Например, в FastAPI проще начать с хэндлеров, которые сразу ходят в SQLAlchemy-модель. Но если сразу заложить сервисный слой, расчёты комиссий и проверку бизнес-правил можно будет тестировать изолированно, не поднимая всю API. Слой репозиториев позволяет в будущем легко заменить Postgres на другую базу или добавить кэш, не переписывая логику приложения.</p><p>Чёткое разделение позволяет быстро находить, где живёт бизнес-логика, где происходит доступ к данным и куда добавлять новые фичи. Продукт растёт, а код остается предсказуемым, удобным для изменений и масштабирования.</p><h2>Ошибка 2. Игнор масштабирования с первого дня</h2><p>Когда проект только запускается, кажется, что про масштабирование можно подумать потом. Приложение обслуживает сотню пользователей, запросы проходят быстро, всё работает. Но как только запускается маркетинг или приходит первый крупный клиент, внезапно всё начинает тормозить, а в коде нет ни одной зацепки, куда безопасно вставить кэш или вынести тяжёлые операции в фон.</p><p>Важно сразу закладывать в архитектуру точки роста, даже если пока они не нужны на каждый день.</p><p>Например:</p><ul><li>Добавить очереди и фоновые задачи через Celery или RQ, если есть риск появления тяжёлых операций (генерация отчётов, массовая рассылка).</li><li>Разносить чтение и запись: даже простое разделение на эндпоинты, где читающие операции не блокируются длительными транзакциями, уже помогает при росте нагрузки.</li><li>Придумать, как кэшировать самые тяжелые запросы: например, использовать Redis, чтобы хранить агрегированные данные, а не считать их каждый раз.</li><li>Закладывать возможность горизонтального масштабирования: не привязывать логику к локальному состоянию приложения, использовать хранилище с возможностью разделения нагрузки, не писать монолит, который нельзя будет разбить на части при росте.</li></ul><p>На практике проект растёт быстрее, чем кажется. Сначала в API добавляется массовая выгрузка CSV, потом приходят интеграции с другими сервисами, а затем накатывается нагрузка от новых пользователей. Если архитектура не подготовлена, команде приходится ставить костыли, переписывать эндпоинты под фоновые задачи или внедрять кэш в экстренном порядке, исправляя баги в проде.</p><p>Закладывая масштабирование в архитектуру с первого дня,  команда экономит себе месяцы переработок и спасается от бесконечных хотфиксов. Это инвестиция, которая позволяет команде развивать продукт спокойно.</p><h2>Ошибка 3. Преждевременное усложнение архитектуры</h2><p>Многие разработчики боятся, что проект не выдержит нагрузку, поэтому с самого старта закладывают сложные паттерны, микросервисы, брокеры событий и сразу три уровня кэширования. Но пока в системе нет ни пользователей, ни подтверждённой бизнес-модели, такие решения снижают скорость разработки и приводят к куче багов.</p><p>Рядовой пример — проект сразу запускается на Kubernetes с несколькими сервисами, которые обмениваются сообщениями через Kafka. В реальности такие проекты вначале требуют десятков часов на поддержание инфраструктуры, а баги приходится искать сразу в нескольких сервисах, между которыми гуляют события. При этом единственная реальная задача в начале — быстро проверить гипотезы и получить первых пользователей.</p><p>Упрощённая архитектура на старте позволяет команде сосредоточиться на продукте: монолит с чёткими слоями (контроллеры, сервисы, репозитории) куда быстрее дорабатывается и легче деплоится, чем микросервисы с отдельными контурами.</p><p>Что можно делать:</p><ul><li>Запускать монолит на FastAPI или Django, а не дробить на микросервисы до появления реальных узких мест.</li><li>Использовать Postgres без брокеров событий, пока не появятся требования к масштабированию.</li><li>Добавлять кэш Redis точечно, когда видна реальная нагрузка, а не вслепую кэшировать каждый запрос.</li></ul><p>Сложные архитектуры требуют времени на поддержку и экспертизу, чтобы не допустить критичных ошибок (например, потерю событий в брокере или гонки данных между сервисами). Поэтому вложения в сложную архитектуру оправданы, когда проект достигает уровня, где без этого уже не обойтись.</p><p>Если команда делает стартап или MVP, преждевременное усложнение архитектуры только тормозит развитие продукта. Гораздо эффективнее заложить возможности для масштабирования (очереди, фоновые задачи, кэш), но держать архитектуру простой до тех пор, пока проект не начнёт расти и не появятся реальные вещи, требующие изменений.</p><h2>Ошибка 4. Непродуманная работа с зависимостями</h2><p>В начале проекта обычно кажется, что зависимости — это просто. Но со временем проект разрастается, зависимости множатся, версии начинают конфликтовать, а обновление одной библиотеки ломает другую.</p><p>Эта ошибка обычно проявляется в нескольких местах. Например, в проект могут без разбора ставиться зависимости про запас или ради одной строчки удобной функции, хотя можно обойтись стандартной библиотекой. Ещё одна частая проблема — зависимости фиксируются слишком жестко, что не дает обновляться безопасно, или наоборот, версии не фиксируются вовсе, и проект начинает падать после автоматического обновления библиотек.</p><p>Чтобы избежать проблем, важно с самого начала заложить порядок в работе с зависимостями:</p><ul><li>Использовать инструмент управления зависимостями, который позволяет контролировать версии и изолировать окружения.</li><li>Разделять зависимости для разработки и продакшена.</li><li>Периодически обновлять зависимости, чтобы не закапываться в старые версии, но делать это контролируемо и с прогоном тестов.</li></ul><p>Например, при работе с Python удобным подходом будет использование Poetry: можно зафиксировать версии зависимостей в pyproject.toml, автоматически создавать lock-файл, следить за актуальностью библиотек и легко пересоздавать окружение. Если проект запускается на CI/CD, можно установить точные версии и сразу выявить, где обновление ломает тесты.</p><p>В длинных проектах непродуманная работа с зависимостями множится в геометрической прогрессии. Любой новый разработчик тратит время на настройку окружения, зависимости конфликтуют, часть библиотек остаётся неиспользованной. Порядок с зависимостями экономит часы и дни, снижает риск падений в проде и влияет на развитие проекта.</p><h2>Ошибка 5. Нет стратегии управления состоянием</h2><p>Состояние — это данные, которые хранятся между запросами или действиями пользователя: корзина, прогресс пользователя, кэшированные результаты запроса, состояние WebSocket-подключений. Если не продумать, как эти данные хранятся, обновляются и синхронизируются, приложение быстро начинает вести себя непредсказуемо.</p><p>На старте часто используют облегченный подход: данные передаются по цепочке вызовов, хранятся в сессии или глобальных переменных, кэшируются в памяти процесса. Это удобно, пока пользователей мало и сервер один. Но при росте нагрузки и масштабировании начинаются проблемы: сессии теряются между инстансами, кэш расходится, данные теряются при перезапуске приложения.</p><p>Чтобы избежать хаоса, управление состоянием нужно продумать заранее:</p><ul><li>Выяснить, какие данные должны храниться между запросами и как долго.</li><li>Решить, где хранить состояние: в базе данных, Redis, сторонних сервисах.</li><li>Сразу заложить сериализацию и валидацию состояния, чтобы избежать рассинхронизации форматов.</li></ul><p>Например, хранение кэша в памяти может подойти для небольших проектов, но если приложение начинает горизонтально масштабироваться, лучше вынести кэш в Redis, чтобы все инстансы работали с единым источником. Для сессий пользователей можно использовать JWT, если нужно масштабирование без общего состояния, или централизованное хранилище сессий, если требуется возможность их отзыва.</p><p>При работе с фронтендом стратегия управления состоянием не менее важна. Например, при использовании React или Vue проект может сначала обходиться локальным состоянием компонентов, но при росте сложности приложение начинает сыпаться из-за рассинхрона между компонентами. Важно заранее заложить подход с централизованным состоянием, а также понять, какие данные держать в состоянии клиента, а какие запрашивать заново.</p><p>Без стратегии управления состоянием проект становится сложным в отладке и поддержке. Грамотное управление состоянием ускоряет разработку и снижает количество багов в будущем.</p><p>А какие ошибки допускаете вы? Делитесь в комментариях или в тг-канале <a href="https://t.me/+a1v-IRDDUqI0MDhi">Веб-страница</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>YAM#3: один TypeScript, что правит всеми</title>
      <link>https://tproger.ru/articles/yam-3-odin-typescript-chto-pravit-vsemi</link>
      <comments>https://tproger.ru/articles/yam-3-odin-typescript-chto-pravit-vsemi?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Шпак]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/yam-3-odin-typescript-chto-pravit-vsemi</guid>
      <description><![CDATA[<p>Мидлварь на TypeScript вызывает разные эффекты в ответ на действия; следующая стадия YAM#3 добавляет типизацию к уже работающему механизму.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/yam-3-odin-typescript-chto-pravit-vsemi">YAM#3: один TypeScript, что правит всеми</a>»</p>]]></description>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Пост пользователя]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 22 Nov 2021 13:56:21 GMT</pubDate>
      <content:encoded><![CDATA[<p>В прошлых статьях мы с вами <a href="https://tproger.ru/articles/yam-1-kogda-hochetsja-napisat-svoju-redux-middleware">написали простейшую мидлварь</a> для redux, а потом <a href="https://tproger.ru/articles/yam2-bolshe-fich-bogu-fich">добавили в неё всяких фич</a>, которые могут быть очень полезны в реальных приложениях. Чтобы сделать её ещё более удобной, сегодня мы займёмся типизацией yam. В отличие от предыдущих статей, здесь я не буду воспроизводить все страдания, ибо даже я сам не смогу полностью описать все лабиринты пещер, в которых плутал. Я просто постараюсь последовательно рассказать, как можно типизировать такую библиотеку, зная конечный результат. Конечно, упоминая проблемы, на которые я натыкался по ходу, чтобы обосновать принятые решения.</p><p>Мир изменился. Я чувствую это в воде. Чувствую в земле. Ощущаю в воздухе. Многое из того, что было, ушло. И не осталось тех, кто помнит.Всё началось с generic аргументов…</p><h2>Один был добавлен для состояния, бессмертного и самого мудрого</h2><p>Используя некоторые базовые типы из redux, мы можем типизировать большую часть аргументов:</p><p>Правда, сейчас вся польза, которую мы получили — это тип значения, возвращаемого из store.getState(). Хочу обратить внимание, что тут необходимо использовать именно MiddlewareAPI, но не Store, так как в мидлвари передаётся не весь интерфейс: replaceReducer и subscribe опускаются.</p><p>Сразу же мы можем прокинуть этот тип в createStateChangedBy:</p><p>При этом нам категорически без разницы, что именно возвращает селектор. Главное, чтобы он принимал конкретное состояние, что мы и обозначаем в типах.</p><p>В целом, такая типизация позволяет нам проверять, что обработчики, переданные в мидлварь, ожидают работать с одним и тем же состоянием.</p><h2>Другой — отдан контексту, великому доставщику утилит без создания циклических зависимостей</h2><p>Его типизировать, на самом деле, несложно. Единственное ограничение, которое <i>хочется</i> добавить: пусть это будет словарь. Чтобы было ясно, что там лежит:</p><p>И вот тут начинается первая магия (я имею в виду undefined as Context). Дело в том, что какой-нибудь абстрактный юзер может явно передать в Context какой-нибудь тип, но при этом опустить второй аргумент функции. И, если переданный тип будет несовместим с undefined, мы окажемся у разбитого корыта с некорректной типизацией. Однако TS прекрасно выводит типы самостоятельно, поэтому если передать хотя бы один явно типизированный обработчик, то типы для состояния и контекста выведутся корректно.</p><p>Я говорю всё это к тому, что при вызове createYam не нужно явно указывать значения generic-параметров. Да и вообще при работе с TS мы скорее хотим этого не делать, нежели наоборот. На моём опыте, автоматическое выведение всегда работает более качественно (и более гибко).</p><h2>А ещё, ещё один тип был сделан для обработчиков, которые больше всего жаждали власти (над побочными эффектами)</h2><p>Основная часть, которая подвержена типизации — это аргумент. Помимо состояния и контекста, которые мы ввели ранее, он также будет зависеть от действия. Ведь это так прекрасно, когда в createYamHandler мы сопоставляем друг другу действие и обработчик и точно знаем тип действия, который в этом обработчике нужно ожидать. Поэтому тип будет выглядеть так:</p><p>Таким образом типизировать обработчик не составляет труда:</p><p>Для внутренней типизации нам также пригодится тип самого обработчика:</p><p>Из всех аргументов обязательным является только тип состояния. Context может не использоваться, а действие, передаваемое обработчику, строго говоря, может быть любым. Поэтому такой набор аргументов кажется мне оптимальным.</p><p>И прежде чем добавить типы в createYam, нам осталось типизировать ещё одно место, связанное с обработчиками: инъекции. Чтобы грамотно разграничить типы действий, используемых внутри createYam, пришлось добавить ещё немного магии:</p><p>Использование type guards будет необходимо, потому что проверка action.type === handlerRequiredType на переменной типа AnyAction | ReturnType&lt;typeof handlerRequired&gt; никоим образом не может сузить тип до ReturnType&lt;typeof handlerRequired&gt;. Это связано с тем, что AnyAction также может иметь в качестве своего типа созданный нами символ, а мы не делаем никаких дополнительных проверок, чтобы это предотвратить.</p><p>Иными словами, совпадение action.type ещё не означает, что это обязательно нечто, что мы вернули из handlerRequired. Однако не будет же пользователь доставать из глубин библиотеки этот символ для создания действия, которое всё сломает. Не будет ведь?</p><p>Ну да ладно. В итоге получаем красиво типизированную мидлварь:</p><p>Единственный момент, который мне не удалось решить — это, так сказать, истинная опциональность контекста. Если вы передаёте контекст в мидлварь, все обработчики должны быть типизированы так, будто его ожидают (даже если не используют). Не получится опустить второй generic аргумент в HandlerArg, иначе createYam не примет такой обработчик, неправильно выведя тип. В целом, это не такая большая трагедия, ведь можно завести локальный тип: type AppHandlerArg = HandlerArg&lt;AppState, AppYamContext&gt; и использовать в обработчиках его, с уже подставленными состоянием и контекстом.</p><h2>Но был сделан ещё один тип, подчинявший себе все другие</h2><p>Ну вот мы и подобрались к самой сочной части нашего проекта. Сама мидлварь уже полностью типизирована и готова к работе, без типов у нас осталась лишь одна единственная вспомогательная функция: createYamHandler, <i>наша прелесссть</i>.</p><p>И начну я с одной большой проблемы, которая начала вызвать у меня боль ещё при попытке организовать типизацию для function createReducer(Record&lt;ActionType, Reducer&gt;): Reducer: невозможно в общем типизировать значения объекта в зависимости от ключа. Ну нельзя, и всё тут. Если бы я заранее написал конкретный объект: да, пожалуйста, TS их видит. Но при попытке обобщить всё просто перестаёт работать. Возможно, в будущем TS предоставит нам для этого какой-то интерфейс, но сейчас его просто нет.</p><p>Но ведь это смотрится странно. Если я говорю, что обработчик будет реагировать на конкретное действие, почему же в самом указанном обработчике действие всё ещё может быть любым? Я об этом:</p><p>Однако если посмотреть на этот код, становится понятно, что нет возможности сопоставить testAction и какую-то рандомную строку 'test', которая, оказывается, соответствует значению свойства у объекта, возвращаемого из функции, которая объявлена на игле, которая в яйце, которое в утке, которая… думаю, вы поняли.</p><p>Сомневаться в разумности команды, занимающейся поддержкой TS, смысла нет, запрос на подобную типизацию — из разряда неадекватных. Но и пользователя можно понять: я точно знаю, какое действие я хочу ожидать в обработчике, и не хотел бы явно писать его дважды.</p><p>К счастью, я не первый, кто добрался до этой проблемы, поэтому у меня было, на что опереться: те же ребята, написавшие <a href="https://redux-toolkit.js.org/">Redux ToolKit</a>, прекрасно справились с задачей. Если вкратце, то суть такова: мы не можем типизировать пару ключ-значение в объекте, но мы можем типизировать функцию, принимающую два аргумента! Дальше всё ограничивается только вашей фантазией. RTK выбрал путь строителя:</p><p>Тут происходит две важные вещи:</p><ol><li>В соответствие кейсу ставится уже не тип в виде строки, но целый `actionCreator`.</li><li>Он содержит в себе не только строку action.type, по которой можно делать условный switch в редьюсере, но и тип для остальных полей в этом действии, который можно явно использовать в функции, переданной вторым аргументом.</li></ol><p>Если очень грубо, тип получается примерно таким:</p><p>Как правило, в подобных решениях при создании actionCreator к функции также добавляется поле type, возвращающее строку, с которой это действие было создано, поэтому по сути мы имеем фабрику и константу для строки типа в одном лице:</p><p>Лично мне больше понравился формат, использованный в <a href="https://deox.js.org/">другой библиотеке</a>, под названием deox. Она делает ровно то же самое, но имеет, на мой взгляд, более приятный интерфейс для редьюсеров:</p><p>Так что для реализации подобной типизации в yam я решил взять вариант с handle за основу.</p><p>Давайте начнём работу над createYamHandler. Для начала нам придётся разобраться с аргументом. Раньше он принимал только словарь, где типу сопоставлялся обработчик, теперь же он может принимать ещё и функцию. Высокоуровнево это выглядит так:</p><p>Здесь появляется некий <i>map builder</i>, из которого мы можем создать нужный нам словарь, да так типизированный (builder, то есть), чтобы для пользователя, создающего пары из actionCreatorов и обработчиков, правильно подставлялись типы действий.</p><p>Сам строитель имеет такой тип:</p><p>Как можно видеть в примере из deox, это некая функция, принимающая волшебный handle и возвращающая нечто, что поможет нам построить наш словарь. Тип возвращаемого значения — деталь чисто внутренняя, поэтому я выбрал [string, Handler] пары как самый простой и прямолинейный вариант (на мой взгляд). Сам же callback содержит в себе основную магическую магию, добавляя индивидуальный generic аргумент для каждого вызова:</p><p>На этом этапе уже может начать становиться страшно, поэтому давайте разберёмся по порядку:</p><ol><li>State и Context — уже привычные нам generic аргументы, которые мы добавили в самом начале; здесь они передаются извне.</li><li>ActionCreator — generic аргумент, который можно явно передать функции при вызове или дать TS самому его вывести; главное, что на каждом вызове он может быть разным, в отличие от State и Context.</li><li>ActionCreator, вообще говоря, может быть любой функцией, возвращающей AnyAction aka { type: extends string; [key: string]: any }, но в аргументе мы также говорим, что нам важно, чтобы эта функция имела на себе свойство type, соответствующее типу возвращаемого действия.</li><li>Второй аргумент — это обработчик, в тип которого мы в первый (и единственный) раз передали третий generic аргумент: тип обрабатываемого действия.</li><li>Возвращаем мы пары тип/обработчик, из которых мы потом будем собирать словарь.</li></ol><p>Это крайне интересный пример типизации, до которого я, наверное, сам никогда и не догадался бы, если не столкнулся бы с такой проблемой, но работает он на ура. Единственное, что в нашем примере осталось неопределённым, это createHandlerMap. Он вроде бы и простой концептуально, но вот там над TS придётся немного поглумиться, потому что он либо слишком умный, либо недостаточно — я так и не понял.</p><p>Самое страшное место здесь, которого, как я понимаю, невозможно избежать — это handler as Handler&lt;State, Context&gt;. Сперва мы доказываем TS, что наш обработчик работает над одним конкретным действием, а потом такие «нет, знаешь, вообще-то над любым».</p><p>Возможно, виной тому необходимость вернуться к Record&lt;string, Handler&gt;, который оставляет эту связь между типами только в run-time. Но без такого преобразования пользователь будет получать ошибку типа «ваш обработчик, конечно, подходит к выставленному ограничению, но может быть инициализирован другим подтипом, который не совместим с вашим» (собственно, с той же ошибкой мы сталкивались при типизации контекста и установке для него значения по умолчанию).</p><p>Так что тут нам придётся сказать TS: «Я знаю, что делаю, не ругай меня, пожалуйста, мне уже есть 18». Ну а fromEntries просто не имеет нормальной типизации, так что там мы его даже не обманываем.</p><p>Собрав всё воедино, получаем такую простыню:</p><p>И эта простыня позволяет нам написать прекрасное</p><p>И каждый обработчик наверняка знает, с каким типом он будет работать.</p><h2>Заключение</h2><p>Вот мы и завершили типизацию очередной мидлвари для redux. Найти репозиторий с полным кодом можно <a href="https://github.com/Jazzmanpw/yet-another-middleware">здесь</a>.</p><p>В ближайшее время я постараюсь подготовить ещё одну статью, в которой соберу различные лайфхаки по использованию библиотеки. Потому что инструмент достаточно свободный, и сделать можно практически всё что угодно (и мы с вами прекрасно знаем, что на инфраструктуре redux это отразилось не лучшим образом), поэтому как-то направить рвения народа лишним не будет.</p><p>И, конечно, спасибо большое, что участвовали вместе со мной в этом замечательном приключении (хоть и как молчаливые зрители). Это были первые мои статьи на техническую тему, но кто знает, может, я войду во вкус и продолжу радовать вас контентом. В любом случае мир тесен, так что где-нибудь и когда-нибудь мы точно с вами встретимся.</p>]]></content:encoded>
    </item>
    <item>
      <title>YAM#2: больше фич богу фич!</title>
      <link>https://tproger.ru/articles/yam2-bolshe-fich-bogu-fich</link>
      <comments>https://tproger.ru/articles/yam2-bolshe-fich-bogu-fich?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Шпак]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/yam2-bolshe-fich-bogu-fich</guid>
      <description><![CDATA[<p>В прошлой части сделали мидлварь, которая вызывает различные эффекты в ответ на определённые действия. Хочу поделиться с вами её развитием.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/yam2-bolshe-fich-bogu-fich">YAM#2: больше фич богу фич!</a>»</p>]]></description>
      <category><![CDATA[Пост пользователя]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 01 Nov 2021 10:54:57 GMT</pubDate>
      <content:encoded><![CDATA[<p><a href="https://tproger.ru/articles/yam-1-kogda-hochetsja-napisat-svoju-redux-middleware/">В прошлой части</a> мы сделали простейшую версию redux мидлвари, которая умеет вызывать различные побочные эффекты в ответ на определённые действия. Однако эволюция на этом не остановилась, и сегодня я хочу поделиться с вами дальнейшим развитием этой идеи.</p><h2>Слушай внимательнее</h2><p>В прошлой статье вы видели пример обработчика, реагирующего на изменение параметров поиска:</p><p>Долгое время наш код жил примерно в таком формате. Однако на практике оказалось, что стоит сделать иначе.</p><p>На панели с фильтрами было несколько разных виджетов: обычный с чекбоксами, виджет с ценами (и полями ввода для произвольных диапазонов), виджет для выбора цвета — перечислять можно долго. И у каждого из этих виджетов была своя логика обработки, которая, в том числе, могла привести к выбору значения, совпадающего с предыдущим (и зачем нам тогда ходить на сервер?).</p><p>В изначальной реализации был волшебный requestBuilder, который использовался в компонентах и создавал новый набор фильтров, попадавший потом в состояние. Но со временем я понял, что это не совсем redux way. Лучше завести набор действий типа filters/checkbox:changed, filters/ranged:changed, filters/color:changed и так далее. Я не буду углубляться в детали реализации, но набралось их порядка десятка. И код обработки изменений фильтров действительно стал гораздо более приятным, но мне очень не хотелось явно перечислять все эти действия в обработчике, ходящем к серверу.</p><p>Более того, перед отправкой запроса происходило сравнение нового запроса с предыдущим, который хранился… в URL! Ох уж этот легаси код, так я его и не вылечил. Но в этот момент стало ясно, что слушаю я не столько действия, сколько изменения состояния. Потом до меня дошло, что можно использовать эту концепцию ещё и для сохранения состояния, будь то location.search или localStorage. Поэтому, помимо реакции на определённые действия, я решил добавить возможность реагировать на изменение состояния. И выглядело это примерно так:</p><p>Сравнение фильтров делается по ссылке, поэтому важно обеспечить, чтобы селекторы правильно отслеживали <i>неизменность</i>. При грамотной организации состояния и использовании reselect (<a href="https://github.com/reduxjs/reselect">тут</a>) это не так уж и сложно сделать. Я реализовывал подобную логику даже для сложных структур (массивы объектов, которые можно сравнить по ключу вместо глубокого равенства, например), но это лежит за пределами текущей темы. Поэтому предположим, что селекторы работают правильно и возвращают нам тот же самый объект, если он по сути своей не изменился.</p><p>Единственная проблема, которая остаётся — это мемоизация. Если вы не знакомы с reselectом, опишу в двух словах: создаваемые селекторы кэшируют предыдущее вычисленное значение, и если входные данные не изменились, то и создавать новый результат нам не придётся.</p><p>Но по умолчанию размер кэша у этих селекторов равен единице, то есть как только на вход поступают новые данные, предыдущий результат выбрасывается. И что тогда? А то, что если несколько обработчиков используют фильтры (например, один для отправки запросов, второй — для записи их в location.search), первый вызванный обработчик собьёт кэш, и второй уже не сможет корректно отработать.</p><p>Можно было бы, кончено, поиграться с кэшированием используемых селекторов, но я нашёл гораздо более элегантное решение, не требующее какой-то особой магии от разработчика, использующего yam.</p><p>Вместо явного вычисления и сравнения прошлого и текущего состояний можно предоставить разработчику функцию, которая будет делать это сама:</p><p>Так давайте напишем свою собственную кэширующую магию, чтобы не сломать селекторы:</p><p>Теперь на одном вызове dispatch мы вызываем каждый селектор только два раза: один раз со старым состоянием и один раз с новым.</p><p>Есть лишь один недостаток, который мне так и не удалось победить. Предположим, у нас есть два зависимых кэширующих селектора:</p><p>Если в обработчике будет использовано stateChangedBy(selectRangeFilters), это собьёт кэш для selectFilters, так как он неявно вызовется с новым состоянием. Это ломает изолированность обработчиков и всю логику сравнения, но я так и не смог понять, насколько это распространённый сценарий, поэтому чинить не стал. Однако это надо иметь в виду. И либо иначе организовывать селекторы (например, убирая мемоизацию там, где она не нужна), либо колдовать с множественным кэшированием для селекторов типа selectFilters.</p><h2>Пиши чище</h2><p>Работая с такой мидлварью, я понял не только как можно её использовать, но и как её использовать не стоит.</p><p>Один из обработчиков содержал в себе крайне много условий и разных по своей природе эффектов. Читать этот код было практически невозможно, а с тестами всё было на порядок хуже. Огромное количество проверяемых кейсов, куча expect(dispatch).toBeCalledWith(action) и страшных моков на всё, что только можно. Много времени было потрачено на обдумывание и обсуждение способов упрощения этого обработчика.</p><p>Первая идея заключалась в том, чтобы разбить этот обработчик на несколько (разделение обязанностей, внезапно). Изначально это не было сделано из-за того, что пришлось бы дублировать некоторые проверки. Но, глядя на получившегося франкенштейна, я понял, что лучше написать одинаковый if в двух-трёх местах, чем <b>это</b>. И решение о разделении было принято. Но такое обсуждение натолкнуло меня на ещё более интересную мысль.</p><p>В моих обработчиках каждая ветка кода приводила к нескольким диспатчам, но, если делить их по зонам ответственности, в каждой из них был только один результат (и это правильно). И, в целом, это звучало как хорошая концепция: «В результате побочного эффекта мы можем получить одно дополнительное событие». Хочу обратить внимание, что не действие, а событие.</p><p>А потом я вспомнил, что часто проблемы с тестированием помогают решить чистые функции. И скажу я вам, что хоть мидлварь по природе своей и предназначена для побочных эффектов, это не значит, что нельзя сделать обработчик чистым. Ну а я человек простой: сказал — написал. Получилось красиво:</p><p>Таким образом мы не только добавили поддержку новомодных слов async/await, но и сделали обработчики проще в плане тестирования, а также ввели ограничение на написание чистых эффектов. Хотя бы касательно изменений состояния. Если эффекты касаются других хранилищ, увы, вызывать (и тестировать) эффекты придётся, как раньше: expect().toBeCalled() и всё в таком духе.</p><h2>Делай прививки</h2><p>Ещё одна фича, которой я никогда не пользовался, но очень часто натыкался на её упоминание в сети — это инъекция отдельных обработчиков в мидлвари при определённых условиях (чаще всего, на определённых страницах или при загрузке определённых компонентов).</p><p>Я считаю, что в маленьких приложениях совершенно необязательно так заморачиваться. Достаточно поставить проверку URL в обработчике и просто выходить из него. Опыт подсказывает, что десяток-другой === на вызов dispatch будет далеко не самой большой проблемой с производительностью. Поэтому в изначальной реализации я даже не думал о подобной логике.</p><p>Однако со временем я осознал, что такая фича полезна как минимум из соображений разбиения кода и уменьшения размера бандла, ведь мы не хотим тянуть в приложение код, который, возможно, даже и не понадобится некоторым пользователям. Ну и раз уж я планирую выложить эту библиотеку в общий доступ, я решил, что инъекции — это must have.</p><p>Наверное, можно придумать десятки вариантов, как добавить обработчик. Я решил опереться на то, что в идеальном приложении всяческие изменения происходят из-за некоторых событий. Значит, и новые обработчики должны добавляться тем же способом.</p><p>С именованием было сложно, так как многословно можно сказать, что <i>нам стал нужен некоторый обработчик</i> и <i>обработчик перестал быть нужным</i>, но это слишком «многабукафф». В итоге остановился на английских required и rejected. Если у вас есть лучшие названия для этих событий, буду рад увидеть их в комментариях.</p><p>С реализацией же всё оказалось не так сложно. Вдохновлялся я add/removeEventListener, которые принимают событие и обработчик и удаляют его по строгому равенству. Тогда внутри кода мидлвари можно хранить их в обычном массиве:</p><p>Действия на добавление обработчиков волшебные, поэтому на них цепочка мидлварей прерывается. Типы объявлены через символы для пущей безопасности: вдруг кому-то понадобится завести handlerRequired действие. Или даже yam/handlerRequired — никто не застрахован. Ну и вставленные обработчики хранятся на уровне store, потому что это кажется концептуально верным, хотя если учесть, что redux строго не рекомендует использовать несколько экземпляров store на приложение, можно было бы и в корень модуля положить. В остальном же всё должно быть понятно.</p><h2>Будь в контексте</h2><p>И напоследок я хотел бы поделиться миниатюрной фичей, важность которой я понял совсем недавно и не то, чтобы до конца. Это больше похоже на ощущение, поэтому здесь мы просто добавим <b>контекст</b> к нашей мидлвари. Реализация будет проще простого:</p><p>Его можно использовать, например, для передачи HTTPClient в обработчики (как <a href="https://github.com/reduxjs/redux-thunk#injecting-a-custom-argument">дополнительный аргумент</a> для redux-thunk), либо же для передачи dispatch, например, на период миграции, когда обработчики не могут просто так взять и отказаться от последовательных диспатчей. Дальше вы ограничиваетесь только вашей фантазией.</p><h2>Заключение</h2><p>На этом этапе yam стала гораздо ближе к варианту, готовому для испытаний в реальной жизни. Она даже стала прекраснее, чем то, что я оставил на своём старом проекте. Когда-нибудь здесь появится ссылка на npm пакет, а пока я предлагаю встретиться в следующий раз, чтобы обсудить последний, но отнюдь не по важности, момент: типизацию.</p>]]></content:encoded>
    </item>
    <item>
      <title>Способы передачи данных между компонентами в React</title>
      <link>https://tproger.ru/translations/react-communication-between-components</link>
      <comments>https://tproger.ru/translations/react-communication-between-components?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Klara Oswald]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/react-communication-between-components</guid>
      <description><![CDATA[<p>Передавать данные между компонентами React можно несколькими способами: через render props и props, через Context и через связку React-Redux с Redux.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/react-communication-between-components">Способы передачи данных между компонентами в React</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Для продолжающих]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 06 Mar 2019 20:08:38 GMT</pubDate>
      <content:encoded><![CDATA[<p>В React есть несколько способов передачи данных между компонентами: Render props / props, Context, React-Redux / Redux.</p><p>В этой статье вы узнаете об этих способах подробно. В каждом из них вы научитесь добиваться двух вариантов взаимодействия:</p><ul><li>от родителя к наследнику;</li><li>от наследника к родителю.</li></ul><h2>Render props / props</h2><p>Один из самых простых способов передачи данных в компоненты — это props (свойства).</p><h3>Что такое prop?</h3><p>Как известно, разметка в компонентах рендерится так:</p><p>Это отобразится как C component</p><p>C фактически отображается в P, так как P является его родительским компонентом. Кроме того, P может отобразить C следующим образом:</p><p>P добавляет дополнительные свойства в тег элемента C. Это очень похоже на &lt;div id="d1"&gt; &lt;/div&gt; в HTML, где id — это атрибут со значением “nnamdi”. Каждый элемент на самом деле является отдельным экземпляром класса HTMLElement.</p><p>Тег &lt;div&gt; анализируется движком рендеринга в браузере, и для каждого тега создаются экземпляры класса HTMLElement. Все теги в HTML имеют соответствующий HTMLElement:</p><p>Каждый HTML*Element выше является подклассом HTMLElement. Эти HTML*Element имеют методы и свойства, которые можно использовать для манипулирования данными и рендеринга.</p><p>В случае выше будет создан HTMLDivElement:</p><p>Имя атрибута будет установлено в объект <i>props</i> вот так:</p><p>и передастся классу компонента C с помощью React.</p><p>Таким образом, компонент C может получить доступ к значению name в объекте <i>props</i> через props.name.</p><p>React основан на JSX, поэтому разметка в P будет скомпилирована в таком виде:</p><p>Первый параметр — это элемент для рендеринга, а второй содержит атрибуты элемента (в виде объектного литерала). CreateElement возвращает литерал, содержащий первый и второй параметры.</p><p>При рендеринге React проверяет, является ли этот литерал на самом деле элементом или же классом. Если это класс, React создаёт его экземпляр, используя ключевое слово <i>new</i>, затем передает этот экземпляр в объект props и вызывает метод render.</p><p>Приведённый выше код показывает, что фактически делает React для рендеринга компонента или элемента.</p><p>Примечание: фрагмент выше не полный, он нужен, чтобы продемонстрировать, как компоненты получают аргумент <i>props</i>.</p><p>Сначала проверяется, является ли objType элементом или классом. Затем IS_ELEMENT(…) проверяет список всех элементов HTML. Если objType соответствует одному из них, то возвращается флаг true: значит objType является элементом. Если IS_ELEMENT(…) возвращает false, то, objType должен быть компонентом класса, тогда он уничтожает props и экземпляр класса из objType. Затем он создаёт новый экземпляр класса, используя ключевое слово new и передавая его в props.</p><p>Таким образом P удаётся отправить данные в C.</p><p>Но как C теперь может отправлять данные обратно в родительский класс P? Это делается с помощью <i>render props</i>.</p><p>Что такое render props? Это концепция, посредством которой функция может быть передана дочерним компонентам в качестве свойства (props). Строка, объект, число или массив могут быть переданы через props дочерним компонентам. Это реализовано следующим образом:</p><p>Здесь функция () =&gt; {log ("render props")} передаётся компоненту C через свойство func. Затем компонент C может ссылаться на неё через func в аргументе props. Это функция выводит render props в консоли.</p><p>Итак, теперь вы знаете, что такое render props (или свойства для рендеринга). Давайте посмотрим, как дочерние компоненты могут использовать их для связи с родительскими.</p><p>Что происходит в этом фрагменте? У компонента P метод вывода связан с его экземпляром. bind(this) говорит JS запустить функцию внутри области видимости класса, объекта или функции. Вывод здесь выполняется за пределами класса P, но он по-прежнему может ссылаться на все свойства и методы, определённые в P.</p><p>Таким образом, метод вывода передаётся в компонент класса C, чтобы получить аргумент <i>props</i> через свойство func.</p><p>К кнопке «Send To Parent» компонента C привязано событие onclick, необходимое для вызова метода вывода, с переданным в него аргументом <i>props</i> через func. Так что теперь this.props.func в C содержит метод вывода в классе P:</p><p>Предположим, кнопка «Send To Parent» нажата. Вызывается this.props.func, ссылаясь на метод вывода в P, далее вызывается сам метод вывода. В консоли будет отображено следующее:</p><p>Теперь C успешно связался со своим родителем P. Можно изменить код, чтобы C отправлял данные в P.</p><p>В этом фрагменте кода в P добавляется состояние, которое содержит свойство count. В C при нажатии кнопки «Send To Parent» генерируется случайное число и передаётся в this.props.func. Метод вывода компонента P перехватывает значение из аргумента evt и обновляет состояние count полученным значением, вызывая this.setState(…).</p><p>Как вы видите, значение P отправилось из его дочернего элемента C.</p><p>Свойство Parent можно изменить, передав функцию компоненту Child и вызвав эту функцию внутри компонента Child.</p><p><i>Props</i> и <i>render props</i> — это одно и то же, но они имеют разные концепции. <i>Render props</i> в основном используется для разделения логики рендеринга между компонентами. Но в данном случае этот метод был использован для передачи данных от дочернего компонента к родительскому.</p><h2>Context</h2><p>Для большинства разработчиков передача props глубоко вложенным компонентам в дереве была бы утомительной. React предоставляет способ определять глобальные данные в одном месте и получать к ним доступ из любого места в приложении.</p><p>Context (контекст) как раз используется в React для обмена данными между глубоко вложенными компонентами.</p><p>В этом фрагменте есть три компонента в дереве: App -&gt; P -&gt; C -&gt; Sub-C. App отображает P, P отображает C, а C отображает Sub-C, который определён как context с именем ColorContext. P заключён в ColorContext.Provider, это обеспечивает доступ к данным, определённым в ColorContext, всем дочерним компонентам P.</p><p>Дочерние компоненты могут получить доступ к данным, определённым в ColorContext, выполнив {this.context}.</p><p>Чтобы сообщить React о намерении передать context от родительского компонента остальным его дочерним элементам, нужно определить два атрибута в родительском классе:</p><ul><li>childContextTypes,</li><li>getChildContext.</li></ul><p>Чтобы извлечь контекст внутри дочернего компонента, нужно определить в нём contextTypes.</p><p>Родительский компонент определяет context, который React использует для передачи данных глобально своим дочерним элементам.</p><p>Как теперь сделать так, чтобы дочерние элементы могли передавать данные своим родителям?</p><p>Дочерние компоненты могут изменять значение context, который будет отражаться во всех вложенных компонентах, включая и родительский компонент.</p><h2>React-Redux/Redux</h2><p>Это своего рода “ремейк” способа Context в React. Redux — это простая библиотека управления свойствами приложения, которая позволяет легко хранить эти свойства в одном месте и изменять их из любого места.</p><p>React-Redux был создан, чтобы сделать Redux легко совместимым с приложениями React.</p><p>Поскольку состояние определяется в одном месте, родительские компоненты могут устанавливать или обновлять его значение, а дочерние могут это фиксировать: связь Родитель-Наследник.</p><p>Аналогично дочерние компоненты могут устанавливать или обновлять значение состояния в хранилище, и оно также будет фиксироваться родительскими компонентами: связь Наследник-Родитель.</p><p>В этом фрагменте создаётся центральное хранилище, в котором содержится свойство count. P может обновить это свойство и дочерний компонент C получит его. Дочерний компонент также может обновить свойство count, и родительский компонент получит его.</p><p>Они могут подключиться к центральному хранилищу, объединившись в функцию connect(...), которая возвращает компонент высшего порядка.</p><h2>Заключение</h2><p>В этом материале вы увидели, как происходит взаимодействие между компонентами React и какими способами можно этого достичь:</p><ul><li>Родитель-наследник,</li><li>Наследник-Родитель.</li></ul><p>В первом разделе вы увидели использование props и render props, они работают с помощью HTML-подобных атрибутов. Context использует центральное хранилище, которое родитель и потомки могут как читать, так и обновлять. React-Redux и Redux делают то же самое, что и Context, компоненты выполняют чтение/запись из центрального хранилища.</p><p>Для дальнейшего освоения React вам может быть полезен данный материал.</p>]]></content:encoded>
    </item>
    <item>
      <title>Краткое руководство по Redux для начинающих</title>
      <link>https://tproger.ru/translations/redux-for-beginners</link>
      <comments>https://tproger.ru/translations/redux-for-beginners?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Никита Прияцелюк]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/redux-for-beginners</guid>
      <description><![CDATA[<p>Redux — менеджер состояний, часто используемым с React. Разберёмся с его внутренним устройством и механизмом работы.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/redux-for-beginners">Краткое руководство по Redux для начинающих</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 08 Dec 2018 13:30:54 GMT</pubDate>
      <content:encoded><![CDATA[<p>Что такое Redux ? Это менеджер состояний. Чаще всего его используют с <a href="https://tproger.ru/articles/kak-stat-react-razrabotchikom-v-2021-dorozhnaja-karta/">React</a>, но его возможности не ограничиваются одной этой библиотекой. Хотя в React есть собственный метод управления состояниями (почитать о нём можно в <a href="https://flaviocopes.com/react/">руководстве по React</a>), он плохо масштабируется. Перемещение состояния вверх по дереву работает для простых приложений, но в более сложных архитектурах изменение состояния производится через свойства (props). Ещё лучше делать это через внешнее глобальное хранилище.</p><p>Библиотека Redux — это способ управления состоянием приложения. Она основана на нескольких концепциях, изучив которые, можно с лёгкостью решать проблемы с состоянием. Вы узнаете о них далее, в этом руководстве по Redux для начинающих.</p><p>Примечание Вы читаете улучшенную версию некогда выпущенной нами статьи.<br />Содержание:</p><ul><li><a href="https://tproger.ru/#part1">Когда нужно пользоваться Redux?</a></li><li><a href="https://tproger.ru/#part2">Использование Redux</a>Неизменяемое дерево состоянийДействияТипы действий должны быть константамиГенераторы действийРедукторы</li><li><a href="https://tproger.ru/#part8">Хранилище</a></li><li><a href="https://tproger.ru/#part9">Поток данных</a></li></ul><h2>Когда нужно пользоваться Redux?</h2><p>Redux идеально использовать в средних и крупных приложениях. Им стоит пользоваться только в случаях, когда невозможно управлять состоянием приложения с помощью стандартного менеджера состояний в React или любой другой библиотеке.</p><p>Простым приложениям Redux не нужен.</p><h2>Использование Redux</h2><p>Разберём основные концепции библиотеки Redux, которые нужно понимать начинающим.</p><h3>Неизменяемое дерево состояний</h3><p>В Redux общее состояние приложения представлено одним объектом JavaScript — state (состояние) или state tree (дерево состояний). Неизменяемое дерево состояний доступно только для чтения, изменить ничего напрямую нельзя. Изменения возможны только при отправке action (действия).</p><h3>Действия</h3><p>Действие (action) — это JavaScript-объект, который лаконично описывает суть изменения:</p><p>Единственное требование к объекту действия — это наличие свойства type, значением которого обычно является строка.</p><h3>Типы действий должны быть константами</h3><p>В простом приложении тип действия задаётся строкой. По мере разрастания функциональности приложения лучше переходить на константы:</p><p>и выносить действия в отдельные файлы. А затем их импортировать:</p><h3>Генераторы действий</h3><p>Генераторы действий (actions creators) — это функции, создающие действия.</p><p>Обычно инициируются вместе с функцией отправки действия:</p><p>Или при определении этой функции:</p><h3>Редукторы</h3><p>При запуске действия обязательно что-то происходит и состояние приложения изменяется. Это работа редукторов.</p><h4>Что такое редуктор</h4><p>Редуктор (reducer) — это чистая функция, которая вычисляет следующее состояние дерева на основании его предыдущего состояния и применяемого действия.</p><p>Чистая функция работает независимо от состояния программы и выдаёт выходное значение, принимая входное и не меняя ничего в нём и в остальной программе. Получается, что редуктор возвращает совершенно новый объект дерева состояний, которым заменяется предыдущий.</p><h4>Чего не должен делать редуктор</h4><p>Редуктор — это всегда чистая функция, поэтому он не должен:</p><ul><li>мутировать аргументы;</li><li>мутировать состояние. Вместо этого создаётся новое состояние с помощью Object.assign({}, ...);</li><li>иметь побочные эффекты (никаких API-вызовов с какими-либо изменениями);</li><li>вызывать нечистые функции. Это функции, результат которых зависит от чего-то кроме их аргументов (например, Date.now() или Math.random()).</li></ul><p>Поскольку состояние в сложных приложениях может сильно разрастаться, к каждому действию применяется не один, а сразу несколько редукторов.</p><h4>Симулятор редуктора</h4><p>Упрощённо базовую структуру Redux можно представить так:</p><h4>Состояние</h4><h4>Список действий</h4><h4>Редуктор для каждой части состояния</h4><h4>Редуктор для общего состояния</h4><h2>Хранилище</h2><p>Хранилище (store) — это объект, который:</p><ul><li>содержит состояние приложения;</li><li>отображает состояние через getState();</li><li>может обновлять состояние через dispatch();</li><li>позволяет регистрироваться (или удаляться) в качестве слушателя изменения состояния через subscribe().</li></ul><p>Хранилище в приложении всегда уникально. Так создаётся хранилище для приложения listManager:</p><p>Хранилище можно инициировать через серверные данные:</p><h3>Функции хранилища</h3><p>Получение состояния:</p><p>Обновление состояния:</p><p>Прослушивание изменений состояния:</p><h2>Поток данных</h2><p>Поток данных в Redux всегда однонаправлен.</p><p>Передача действий с потоками данных происходит через вызов метода dispatch() в хранилище. Само хранилище передаёт действия редуктору и генерирует следующее состояние, а затем обновляет состояние и уведомляет об этом всех слушателей.</p><p>Советуем начинающим в Redux прочитать нашу статью <a href="https://tproger.ru/translations/react-communication-between-components/">о других способах передачи данных</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как задеплоить веб-приложение на связке React и Redux за 10 минут</title>
      <link>https://tproger.ru/articles/reactjs-redux-webapp-10-minutes</link>
      <comments>https://tproger.ru/articles/reactjs-redux-webapp-10-minutes?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Ярослав Сарницкий]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/reactjs-redux-webapp-10-minutes</guid>
      <description><![CDATA[<p>Веб-приложение на React и Redux можно подготовить к демонстрации: установить ReactJS, запустить его локально и разместить проект в AWS S3.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/reactjs-redux-webapp-10-minutes">Как задеплоить веб-приложение на связке React и Redux за 10 минут</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 30 Jul 2017 17:55:30 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вы ищете способ быстро продемонстрировать коллегам или заказчикам идею своего веб-приложения, развернув его на сервере, эта статья для вас.</p><p>Вы получите аналогичное приложение после выполнения этих действий:</p><ul><li>локальная установка ReactJS при помощи шаблона;</li><li>настройка «корзины» AWS (Amazon Web Services) S3;</li><li>создание учетных данных пользователя AWS для загрузки файлов на S3;</li><li>развертывание шаблона на AWS;</li><li>проверка работоспособности.</li></ul><p>Необходимые инструменты:</p><ul><li><a href="https://nodejs.org/en/download/">Node.js</a> версии 6.0 или выше;</li><li><a href="https://yarnpkg.com/lang/en/docs/install/">Yarn</a>;</li><li>Аккаунт <a href="https://aws.amazon.com/">AWS</a> (бесплатного будет достаточно). Для интересующихся у нас есть <a href="https://tproger.ru/translations/aws-in-plain-russian/">шпаргалка по веб-сервисам Amazon</a>.</li></ul><h2>1. Установка ReactJS</h2><p>Клонируем шаблон (запустите команду в терминале), заменив «NameOfApp» на имя своего приложения:</p><p>Устанавливаем все библиотеки:</p><p>Запускаем React по локальному адресу http://localhost:3000/ (запуск может занять несколько секунд):</p><figure><img src="https://media.tproger.ru/uploads/2017/07/1-8.png" alt="" /></figure><h2>2. Настройка корзины AWS S3</h2><p>Входим в свой аккаунт на AWS и выбираем S3:</p><figure><img src="https://media.tproger.ru/uploads/2017/07/2-4.png" alt="" /></figure><p>Нажимаем «Create bucket» и вводим имя (например: onederful-quickstart). Нажимаем «Далее» на всех остальных шагах и создаем <a href="https://aws.amazon.com/ru/s3/details/">корзину</a> (bucket, в русскоязычных источниках также можно встретить термин «бакет»):</p><figure><img src="https://media.tproger.ru/uploads/2017/07/3-2.png" alt="" /></figure><p>Теперь открываем только что созданную корзину:</p><figure><img src="https://media.tproger.ru/uploads/2017/07/4-2.png" alt="" /></figure><p>После появления всплывающего окна нажимаем на «Properties»:</p><figure><img src="https://media.tproger.ru/uploads/2017/07/5-2.png" alt="" /></figure><p>Нажимаем на «Static website hosting» и вводим «index.html» в каждом из полей «Index document» и «Error document». Теперь у нас есть общедоступный URL:</p><figure><img src="https://media.tproger.ru/uploads/2017/07/6-3.png" alt="" /></figure><p>Переходим на вкладку «Permissions», вместо [YOUR BUCKET NAME] вписываем свое название проекта:</p><figure><img src="https://media.tproger.ru/uploads/2017/07/7-2.png" alt="" /></figure><h2>3. Создаем учетные данные пользователя AWS для загрузки файлов на S3</h2><p>В консоли управления AWS нажимаем на «IAM» (Identity Access Manager):</p><figure><img src="https://media.tproger.ru/uploads/2017/07/8-3.png" alt="" /></figure><p>Переходим на вкладку «Users», находящуюся на боковой панели, и добавляем пользователя с именем «s3-admin»:</p><figure><img src="https://media.tproger.ru/uploads/2017/07/9-2.png" alt="" /></figure><p>Прикрепляем «AmazonS3FullAccess policy»:</p><figure><img src="https://media.tproger.ru/uploads/2017/07/10-2.png" alt="" /></figure><p>После создания пользователя сохраняем идентификатор доступа и секретный ключ (например, в блокноте) — они будут использоваться на последнем этапе этого руководства:</p><figure><img src="https://media.tproger.ru/uploads/2017/07/11-2-1024x379.png" alt="" /></figure><h2>4. Публикуем шаблон на AWS</h2><p>Замените следующие данные в файле tools/s3-upload.js:</p><ul><li>YOUR_BUCKET_NAME — на название корзины (со второго шага);</li><li>YOUR_AWS_ACCESS_KEY — на свой идентификатор доступа (с третьего шага);</li><li>YOUR_AWS_SECRET_KEY — на свой секретный ключ (с третьего шага).</li></ul><figure><img src="https://media.tproger.ru/uploads/2017/07/12-2.png" alt="" /></figure><p>Публикуем приложение:</p><h2>5. Проверяем работоспособность и начинаем создавать приложение</h2><p>Проверьте работоспособность приложения в вашем браузере. Если все работает, вы можете приступать к созданию логики вашего веб-приложения.</p><p>Настройка AWS должна выполняться только один раз, поэтому после внесения каких-либо изменений в ваше приложение можно просто запустить команду deploy, и в течение нескольких секунд изменения вступят в силу.</p>]]></content:encoded>
    </item>
  </channel>
</rss>