1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React Native / Expo】Fabric有効化で発生する `java.lang.IllegalStateException: addViewAt` クラッシュとの闘いと解決法

Posted at

はじめに

Expo SDK 52 (React Native 0.76) にアップグレードし、Androidで New Architecture (Fabric) を有効にしたところ、アプリ起動時や画面遷移時に java.lang.IllegalStateException: addViewAt という謎のクラッシュに遭遇しました。
数日間にわたるデバッグの末、意外な原因と解決策が見えてきたので共有します。

環境

  • Expo SDK 52
  • React Native 0.76.x
  • Platform: Android (Fabric Enabled)
  • Navigation: Expo Router

エラー内容

アプリを起動し、ログイン後のホーム画面を表示しようとすると以下のエラーでクラッシュする現象が発生しました。

java.lang.IllegalStateException: addViewAt: failed to insert view [ViewName] at index [X]
    at com.facebook.react.uimanager.ViewGroupManager.addView(ViewGroupManager.java:X)
    ...

試したこと(効果が薄かったもの)

当初は「特定のUIライブラリがFabricに対応していないのでは?」と疑い、以下を試しましたが解決しませんでした。

  1. react-native-screens の無効化: enableScreens(false) を試したが変化なし。
  2. react-native-safe-area-context の削除: レイアウト崩れ覚悟で外したがクラッシュは継続。
  3. expo-linear-gradient 等の削除: グラデーションやBlurなどの描画系ライブラリを外しても改善せず。
  4. InteractionManager による遅延レンダリング: 画面遷移のアニメーションが終わるまで描画を待機させようとしたが、逆に不安定になったり、問題が隠蔽されたりした。

原因の特定プロセス

「特定のコンポーネント」ではなく「アプリの構造」に問題があると考え、以下の手順で切り分けを行いました。

  1. 最小構成にする: app/_layout.tsx から全ての Provider (AuthProvider, ThemeProvider 等) を削除し、単なる <Slot /> だけにする。 -> 起動成功
  2. Providerを戻す: 認証やテーマのProviderを戻す。 -> 起動成功
  3. Navigationを戻す: Slot から StackTabs へと戻していく。 -> ここで怪しい挙動
  4. 画面の中身を空にする: タブ内の画面を <View /> だけにする。 -> 起動成功
  5. 中身を少しずつ戻す: ヘッダー、リスト、モーダル...と戻していく中で、JSエラー(dayjsの初期化漏れ) が発生していることを発見。

真の原因

今回のケースでは、単一の原因ではなく、以下の複合要因でした。

  1. 隠れていたJSエラー:
    dayjs のプラグイン (timezone, utc) の初期化が正しく行われておらず、レンダリング中に TypeError が発生していた。
    Legacy Architectureでは「赤画面(RedBox)」で止まっていたかもしれないが、Fabric環境下では、このJSエラーによるレンダリング中断が、Native側のShadow TreeとView Hierarchyの不整合を引き起こし、結果として addViewAt クラッシュとして現れていた可能性が高い。

  2. 複雑なレンダリング制御:
    InteractionManager.runAfterInteractions を多用してレンダリングを制御しようとしていたが、これがFabricの同期的な描画プロセスと競合し、Viewの挿入タイミングをおかしくしていた。

解決策

1. JSエラーの徹底修正

dayjs などの初期化ロジックを app/_layout.tsx の最上位(コンポーネントの外)に移動し、確実に初期化されるようにしました。

// app/_layout.tsx
import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";

// コンポーネント定義の前に確実に実行
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.tz.setDefault("Asia/Tokyo");

export default function RootLayout() {
  // ...
}

2. レンダリングロジックの単純化

InteractionManager による無理な遅延表示をやめ、Reactの標準的なライフサイクル (useEffect) に任せるシンプルな実装に戻しました。

3. コンポーネントの再構築

一度崩して、最小構成から積み上げ直すことで、不整合を起こしていた古いキャッシュや状態をクリアにしました。

教訓

  • Fabricでの addViewAt は「Viewの不整合」: 必ずしもネイティブモジュールのバグではなく、JS側のロジックミス(特に初期化や条件付きレンダリング)が原因で、Native側が混乱している場合がある。
  • JSエラーを疑え: Nativeクラッシュしていても、その直前にJS側でエラーが出ていないか adb logcat で徹底的に確認する。
  • シンプル・イズ・ベスト: Fabricはパフォーマンスが良い反面、挙動が厳格。Legacy時代のハック(InteractionManagerでの回避など)は逆に足かせになることがある。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?