услуга: Мобильная разработка

React Native без легаси: как мы переехали на Expo 54 и отказались от Redux

-16 810
строк кода
до 6
сжатие слоёв FSD
1545
файлов затронуто
React Native без легаси
Задача
За два года приложение на React Native обросло сложной архитектурой и нативными модулями. Даже небольшие правки вызывали ошибки сборки, а Redux требовал много лишнего кода. Запутанная навигация усложняла поддержку проекта, а онбординг новых разработчиков занимал около трёх дней.
Решение
За пять месяцев переработали 1545 файлов и сократили код на 16 810 строк. Упростили архитектуру FSD с 7 до 6 слоёв, убрали дублирующиеся UI-компоненты и разделили клиентское и серверное состояние. В результате поддержка приложения стала проще, а онбординг новых разработчиков сократился с трёх дней до нескольких часов.

Какая была проблема

Наше мобильное приложение разрабатывалось около двух лет как bare React Native приложение. Оно росло силами разных разработчиков и обрастало кастомными нативными модулями.

К концу 2025 года это вылилось в конкретную боль:

  • Любая мелкая правка могла упереться в конфликт нативных зависимостей в CocoaPods или Gradle-ошибку сборки на Android 12.
  • Redux Toolkit в связке с FSD превратился в генератор бойлерплейта: чтобы добавить одно поле, нужно было создать слайс, экшены, thunk, селекторы и зарегистрировать редьюсер в корне.
  • Навигация была «матрёшкой» из навигаторов, размазанной по отдельному слою — разобраться, как пользователь попадает из чата в детали задачи, было тяжело.
  • Онбординг нового разработчика занимал около трёх дней: ручной pod install, настройка Gradle, объяснение внутреннего устройства навигации и стейта.

Зачем мы на это пошли?

Чинить проект «поверх» точечными патчами было дороже и рискованнее, чем пересобрать фундамент. Инкрементальный апгрейд грозил утонуть в ручном разрешении конфликтов в project.pbxproj и build.gradle. Поэтому мы выбрали путь greenfield: новый проект с нуля, перенос в него JS/TS-кода и постепенное вырезание легаси.

Цели были простые:

  1. Избавиться от боли с нативной сборкой и вернуть контроль над платформенным кодом.
  2. Убрать бойлерплейт Redux и разделить клиентское и серверное состояние.
  3. Сделать архитектуру понятной для любого React-разработчика с рынка, а не только для тех, кто рос вместе с проектом.

Работа заняла пять месяцев и затронула 1545 файлов: добавлено 16 700 строк, удалено 33 510, чистый результат — минус 16 810 строк кода.

Платформа и сборка

Раньше нативные папки ios/ и android/ жили прямо в Git, и любое добавление SDK означало ручные правки в AppDelegate.mm и build.gradle. Версии компиляторов плавали от машины к машине — то Kotlin Gradle Plugin конфликтовал, то iOS deployment target не совпадал с ожиданиями библиотеки. В такой обстановке онбординг нового разработчика растягивался на добрые три дня: pod install, ручная настройка Gradle, попытки понять, почему на его машине не собирается то, что собиралось у всех остальных.

Мы перевели управление нативной частью на декларативные рельсы. Нативные папки теперь генерируются командой prebuild и лежат в .gitignore, а вся конфигурация SDK, разрешений, иконок и bundle ID — в одном файле app.json. Настройка внешних SDK, вроде Firebase, оформляется Config Plugins вместо ручных вставок в нативный код, а версии компиляторов фиксируются декларативно через expo-build-properties. В итоге онбординг значительно сократился: клонировал репозиторий, запустил npm run dev — и работаешь.

Навигация

Навигация раньше держалась на пяти разных пакетах React Navigation и была императивной «матрёшкой» навигаторов — около 683 строк кода в отдельном слое, разобраться в которых с наскока было тяжело. Логика auth-гарда при этом размазывалась по разным навигаторам суммарно на 250 строк, и любое изменение в правилах доступа приходилось искать по всему стеку.

Мы заменили всё это на Expo Router — декларативный файловый роутинг, где структура экранов буквально повторяет структуру папок: защищённая и гостевая зоны разведены по каталогам (auth) и (unauthorized), а модальные окна вынесены в отдельный прозрачный роут. Прежние 683 строки превратились в лаконичные файлы-роуты, а вся логика auth-гарда — в компактный реактивный хук.

Стейт-менеджмент

На Redux Toolkit с redux-persist держалось и клиентское, и серверное состояние сразу. Чтобы добавить одно поле, нужно было создать слайс, экшены, thunk, селектор и зарегистрировать редьюсер в корне — а хранилище AsyncStorage работало через асинхронный JS-мост, из-за чего при обновлении данных по WebSocket приходилось либо перезапрашивать всё по HTTP, либо городить ручную инвалидацию.

Мы заменили эту связку на Zustand с Immer для лёгкого UI-стейта и React Query для серверных данных, а AsyncStorage — на react-native-mmkv с синхронным доступом к хранилищу. Добавить новое поле теперь — это одна функция в сторе, а события по WebSocket пишутся напрямую в кэш React Query, без лишних HTTP-запросов.

Второй эффект — мы разделили ответственность: Zustand хранит только лёгкий локальный UI-стейт, а кэшированием, пагинацией, инвалидацией и вебсокет-обновлениями серверных данных занимается React Query. При логауте кэш просто стирается вызовом queryClient.clear(), исключая утечку данных между аккаунтами.

Архитектура FSD

Раньше проект жил в семи слоях FSD, и отдельный слой processes отвечал только за навигационные стеки и авторизационные переключатели. Как только навигация переехала в файловую структуру Expo Router, а auth-гард — в корневой лейаут, этот слой полностью потерял смысл, и мы его удалили — архитектура сжалась до шести слоёв.

Вторая проблема, которую решили на уровне архитектуры — циклические импорты между мелко раздробленными фичами и сущностями. Мы сгруппировали код вокруг крупных доменов (например, «Задача», «Сообщение», «Регламент»), где каждый домен инкапсулирует свои API-запросы, локальный UI-стейт и вспомогательные компоненты. Фичи и виджеты теперь импортируют такие домены сверху вниз, а сами домены остаются независимыми друг от друга — циклы исчезли.

Заодно провели ревизию UI Kit: множество дублирующихся элементов заменили на единую систему базовых элементов, оптимизировав рендеринг тяжелых экранов.

Чтобы вычистить «файлы-призраки» и неиспользуемые экспорты, оставшиеся после переноса полутора тысяч файлов, подключили статический анализатор кода — он строит граф импортов от точек входа и находит мёртвый код, неиспользуемые типы и лишние npm-пакеты.

Нативные модули

Устаревшие сторонние библиотеки заменили на официальные модули экосистемы Expo (файловая система, пуши, тактильный отклик и др.), которые поддерживаются платформой из коробки.

Что получили на выходе

В цифрах:

  • Удалено свыше 33 000 строк кода, добавлено 16 700 — чистый результат минус 16 810 строк.
  • Затронуто 1545 файлов за пять месяцев работы.
  • Архитектура сжалась с 7 слоёв FSD до 6.
  • Множество дублирующихся UI-компонентов заменены на единую систему базовых элементов.

По сути:

  • Быстрый онбординг. Не нужно днями объяснять новичкам устройство навигации и стейта — теперь это индустриальный стандарт (Expo, Zustand, React Query), знакомый любому React-разработчику.
  • Предсказуемый стейт. Клиентские и серверные данные разделены: Zustand отвечает за лёгкие локальные настройки, React Query — за весь жизненный цикл серверных данных.

Главный итог не в количестве удалённых строк, а в том, что в проект больше не страшно заходить. Онбординг занял часы вместо дней а стейт стал предсказуемым. Впереди ещё много работы над функциональностью, но фундамент под неё теперь чистый и надёжный.

Читайте также:

 

Оставить заявку

Проконсультируем по всем вопросам

Оставить заявку

Читать еще...

ProSMD

Автоматизировали формирование коммерческих предложений внутри 1С-Битрикс

Фитнес-клуб World Class

1606 конверсий, CPL меньше в 2,3 раза, конверсий > в 1,6 раз
Бот для Макс
Бот для Telegram

Панель коммуникатора для автошколы Вектор

Система автоматизации контроля задач
Напишите нам