Skip to content

React Native 0.82.1 → 0.87.0 - #31

Merged
maximcoding merged 6 commits into
masterfrom
chore/rn-0.87-upgrade
Aug 26, 2026
Merged

React Native 0.82.1 → 0.87.0#31
maximcoding merged 6 commits into
masterfrom
chore/rn-0.87-upgrade

Conversation

@maximcoding

Copy link
Copy Markdown
Owner

Стартер стоял на 0.82.1 от октября 2025. Актуальная — 0.87.0 от 11.08.2026, пять минорных релизов спустя; 54 пакета из 60 отставали.

Проверено на обеих платформах: собирается, ставится и доходит до экрана Home с живыми данными — Android на Pixel 8, iOS в симуляторе iPhone 17 Pro.

Как это делалось

Сначала снят базовый прогон до обновленияtsc 0 ошибок, lint чист, 41 тест, assembleDebug успешен. Без него нельзя было бы отличить свои поломки от уже существовавших. Каждое утверждение о совместимости проверялось в реестре npm или падающей сборкой, а не по памяти.

Что двигалось и почему

Связанный набор — то, что не умеет двигаться поодиночке: RN требует react ^19.2.3; Reanimated 4.6.0 требует react-native 0.83–0.87 и worklets 0.12.x; пакеты @react-native/* привязаны к версии рантайма.

react-native-gesture-handler 2.29.1 → 3.2.1 попал сюда по факту, а не по плану: он вообще не компилируется с RN 0.87 — его Kotlin-исходники зовут getRootViewTag() и getZIndexMappedChildIndex(), которых во внутренностях больше нет. Он был в списке исключённых мажоров, пока сборка не доказала обратное. Приложение использует ровно один его экспорт, GestureHandlerRootView.

Навигация остаётся на 7.x по правилу самого репозитория (docs/TODO.md): v8 всё ещё 8.0.0-alpha.43.

react-native-mmkv и react-native-nitro-modules не тронуты решением владельца — это связка, где docs/development.md обещает ошибки CxxPart, и реестр это подтверждает: mmkv 4.3.2 ждёт nitro 0.35.9, а последний в npm — 0.37.0.

Дюжина мажоров сознательно не взята — Babel 8, TypeScript 7, Jest 30, webview 14, bootsplash 7, i18next 26 и прочие. Babel 8 просто несовместим (@react-native/babel-preset@0.87 зависит от @babel/core ^7.25.2); остальные — отдельные оси, и смешать их с обновлением RN значило бы сделать любую поломку неатрибутируемой.

Что сломалось в прикладном коде

Три настоящих удаления API, каждое сверено с установленным рантаймом:

  • StyleSheet.absoluteFillObject больше нет — теперь сам absoluteFill и есть объект;
  • StatusBar лишился backgroundColor и translucent: Android рисует edge-to-edge всегда, у полосы нет своего фона. Два экрана просили прозрачную полосу под градиент — это стало умолчанием, так что их statusBarProps ушли целиком;
  • AppState.currentState теперь может быть null/undefined — до первого события состояние честно неизвестно.

Android — вся цена обновления

RN отдаёт свой gradle-плагин composite-сборкой, и тот несёт AGP 9.2.1. Проект, который объявлял собственную версию, спор не выигрывал — применялся 9.x, а объявлен был 8.13.2, и это вылезало как «plugin is no longer required» и «does not specify compileSdk». Корневой build.gradle приведён к форме шаблона 0.87, где версиями владеет сам RN.

Два флага из шаблона оказались несущими, а не косметикой:

android.builtInKotlin=false   # иначе AGP 9 запрещает kotlin-плагин,
                              # а его применяют ДЕВЯТЬ библиотек набора
                              # в самых свежих версиях
android.newDsl=false          # они же написаны на старом DSL

Gradle → 9.4.1 (AGP 9 на меньшем не работает), build-tools и NDK — к шаблонным, proguard-android.txt-optimize (первый AGP 9 отвергает за -dontoptimize).

Один обходной путь, намеренно узкий: io.github.zhongwuzw 2.4.0 отдаёт пространство имён com.tencent.mmkv сразу из двух артефактов, слияние манифестов AGP 9 это не принимает; в 2.4.1 разведено. Поднят только нативный артефакт — npm-пакеты mmkv и nitro остались нетронутыми.

Инструменты

Пресет Jest уехал из корня react-native в отдельный пакет @react-native/jest-preset — без него jest не стартовал вовсе. Babel-плагин Reanimated в 4.x — четыре строки реэкспорта из react-native-worklets/plugin; путь исправлен на настоящий.

Проверка

tsc --noEmit 0 ошибок
biome check чисто
Тесты 41/41, включая App.test.tsx — он поднимает всё дерево
check:icons / check:imports пройдены
Android assembleDebug ✅, установлено на Pixel 8, дошло до Home
iOS BUILD SUCCEEDED ✅, симулятор iPhone 17 Pro, дошло до Home

Минимум iOS остаётся 15.1 — RN 0.87 по-прежнему требует именно его; предположение, что нужно поднимать, оказалось неверным.

Осталось за рамками

  • Дюжина независимых мажоров выше — каждый отдельной задачей.
  • Мёртвые ссылки на удалённый docs/OPERATIONS.md в README, AGENTS.md и CLAUDE.md — существовали до этой ветки, не трогал, чтобы не размывать диф.
  • 12 веток dependabot: большинство закроется само, они про транзитивные зависимости RN CLI и Metro, которые здесь пересобраны.

🤖 Generated with Claude Code

maximcoding and others added 6 commits August 26, 2026 00:42
The starter sat on 0.82.1 from October 2025; 0.87.0 shipped 11.08.2026, five
minors later, and 54 of 60 packages were behind.

What moves here moves because it cannot move alone. RN 0.87 requires React
^19.2.3; Reanimated 4.6.0 requires react-native 0.83-0.87 and worklets 0.12.x;
the @react-native/* toolchain packages are version-locked to the runtime. Every
one of those was verified against the registry rather than assumed.

react-native-gesture-handler goes to 3.2.1 for the same reason, and only after
the evidence said so: 2.29.1 does not compile against RN 0.87 at all — its
Kotlin sources call getRootViewTag() and getZIndexMappedChildIndex(), which the
new internals no longer expose. It was on the deliberately-excluded list until
the build proved it belonged to the locked set. The app touches exactly one of
its exports, GestureHandlerRootView, so the major carries no app-level risk.

Navigation stays on 7.x by the repository's own rule (docs/TODO.md): v8 is
still 8.0.0-alpha.43, so only minors within 7 are taken.

react-native-mmkv and react-native-nitro-modules are untouched by the owner's
decision — that pairing is where docs/development.md promises CxxPart failures,
and the registry bears it out: mmkv 4.3.2 expects nitro 0.35.9 while npm's
latest nitro is 0.37.0.

Also dropped react-native-worklet (singular) — an empty 0.0.0 stub imported
nowhere and confusable with the real react-native-worklets.

The dozen unrelated majors now available — Babel 8, TypeScript 7, Jest 30,
webview 14, bootsplash 7, i18next 26 and the rest — are deliberately not here.
Babel 8 is outright incompatible (@react-native/babel-preset 0.87 depends on
@babel/core ^7.25.2); the others are separate axes, and mixing them into an RN
bump would make any breakage unattributable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three genuine removals, each confirmed against the installed runtime rather
than guessed at.

StyleSheet.absoluteFillObject is gone; absoluteFill is now the object itself,
so the spread in HalfSheet reads the same and means the same.

StatusBar lost backgroundColor and translucent. Android draws edge-to-edge
always now, so the bar has no background of its own to colour — what shows
behind it is whatever the screen paints there, and ScreenWrapper already paints
it via the themed background on its SafeAreaView. The two screens that asked
for a transparent bar under their gradient were asking for what is now the
default, so their statusBarProps go away entirely rather than being rewritten.

AppState.currentState is now typed as string, null or undefined: before the first
event the state may genuinely be unknown, and 'unknown' is the value
AppStateStatus already carries for that case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…it work

The Android build was the whole cost of this upgrade, and none of it was
guesswork — each step is what a failing build said, checked against the
official 0.87 template.

The root build.gradle no longer pins AGP. React Native ships its gradle plugin
as a composite build, and that plugin carries its own AGP 9.2.1; a project that
also declared its own version did not win the argument, it just produced
nonsense — the applied plugin was 9.x while the declared one was 8.13.2, and
the errors read as plugin is no longer required and does not specify
compileSdk. The template form lets React Native own those versions, which is
the only arrangement where they agree.

android.builtInKotlin=false and android.newDsl=false are the two flags the 0.87
template sets, and they are load-bearing rather than tidy-up. AGP 9 otherwise
forbids the org.jetbrains.kotlin.android plugin — which nine libraries in this
dependency set still apply, screens, safe-area-context, gesture-handler, mmkv
and nitro-modules among them, at their newest published versions — and demands
the new DSL, which none of them are written in.

Gradle goes to 9.4.1 because AGP 9 refuses to run on less, and buildTools and
NDK move to the template's, since AGP 9 no longer promises to support the older
ones. Kotlin stays at the project's 2.2.21, above the template's 2.2.0.
proguard-android.txt is rejected outright by AGP 9 for carrying -dontoptimize.

One workaround, and it is deliberately narrow. io.github.zhongwuzw 2.4.0 ships
the com.tencent.mmkv namespace from two artifacts at once, which AGP 9's
manifest merger will not accept; 2.4.1 separates them. Only that native
artifact is forced. The npm packages react-native-mmkv and
react-native-nitro-modules stay exactly where the owner left them.

Verified: assembleDebug succeeds, the APK installs on a Pixel 8, and the app
reaches Home with live data — the same thing maestro/smoke-android.yaml asserts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ut of reanimated

React Native 0.87 no longer ships a jest preset at its own root — it is a
separate package now, @react-native/jest-preset, versioned with the runtime.
Without it jest refused to start at all: Module react-native should have
jest-preset.js at the root.

The reanimated babel plugin is the same shape of move. react-native-reanimated
/plugin in 4.x is four lines re-exporting react-native-worklets/plugin, so
pointing at the shim still worked; pointing at the real one is where it lives
now, and shims do not last.

All 41 tests pass on both changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pod install under the upgraded dependency set, with CocoaPods 1.17. The
PrivacyInfo and pbxproj touches are the installer's own.

The deployment target is left at 15.1: React Native 0.87 still reports
min_ios_version_supported 15.1, so the assumption that it needed raising was
wrong and nothing here changes it.

Verified: xcodebuild for the simulator reports BUILD SUCCEEDED, the app
installs on an iPhone 17 Pro simulator and reaches Home with live data.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three places hard-coded 0.82.1 — the README badge, the README feature list and
the stack line in .claude/CLAUDE.md — and all three now say 0.87.0.

The rule forbidding version changes is rewritten rather than deleted. What it
was protecting is real: an agent that bumps a dependency to dodge a bug leaves
a codebase nobody can reason about. What it could not express is that an
upgrade is sometimes the work itself. It now forbids the initiative, not the
act, and says why version changes must not ride along inside an unrelated
diff — a break that follows a mixed change cannot be attributed to anything.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@maximcoding
maximcoding merged commit 4b0ac69 into master Aug 26, 2026
3 checks passed
@maximcoding
maximcoding deleted the chore/rn-0.87-upgrade branch August 26, 2026 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant