RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-04-19中級

Rorkアプリでローディングが終わらない — 非同期処理後にUIが更新されない問題を解決する

Rorkアプリでローディングスピナーが消えない・データが表示されない問題の原因を体系的に解説。useState・useEffect・try/catch/finallyの落とし穴と修正コードをパターン別に紹介します。

Rork515トラブルシューティング77useState2useEffect2非同期処理API連携3デバッグ22

「APIから正常にデータが返ってきているのに、画面がずっとくるくる回っている」——Rorkで作ったアプリを実機テストしていると、この状況に一度は遭遇するのではないでしょうか。

エラーメッセージが出ているわけでもなく、ログを見ると値は取れています。でも画面は更新されありません。原因が掴みにくく、AIに修正を依頼しても同じ問題が繰り返される……というのが、このタイプのバグの厄介なところです。

ここではRorkが生成するReact Nativeコードでローディングが終わらない・UIが更新されない問題を、パターン別に整理して解説します。コードの読み方がわからなくても対処できるよう、修正のポイントをできる限り具体的に説明します。

なぜこの問題が起きるのか

Rorkが生成するコードは、データ取得の処理を非同期(async/await)で書きます。処理の流れはざっくりこうなります。

  1. 画面が表示される
  2. isLoading = true にしてスピナーを表示
  3. APIなどからデータを取得する
  4. isLoading = false にしてスピナーを消し、データを表示

問題は、3のステップで何かが起きたとき、4のステップが実行されないケースがあることです。「データ取得に失敗したとき」「エラーが発生したとき」「取得結果が空だったとき」——こうした例外的な状況で isLoadingfalse に戻らないと、スピナーが永遠に回り続けます。

パターン1: エラー発生時にローディングが解除されない

最も多い原因です。try/catch を使っているのに、finally を書いていないパターンです。

// エラーが起きるとこのパターンはローディングが解除されない
const fetchData = async () => {
  setIsLoading(true);
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    setItems(data);
    setIsLoading(false); // エラーが起きるとここに到達しない
  } catch (error) {
    console.error(error);
    // isLoading を false にする処理が抜けている
  }
};

エラーが発生すると catch ブロックに処理が飛び、setIsLoading(false) が実行されないまま終わります。修正は finally を使います。

// finally で必ずローディングを解除するパターン
const fetchData = async () => {
  setIsLoading(true);
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    setItems(data);
  } catch (error) {
    console.error('データ取得エラー:', error);
    setError('データの取得に失敗しました');
  } finally {
    setIsLoading(false); // 成功・失敗を問わず必ず実行される
  }
};

finally ブロックに書いた処理は、try が成功しても catch に入っても、必ず実行されます。ローディング解除の処理はここに書くのが原則です。

Rorkにこの問題を修正してもらうときは「fetchData関数のtry/catchにfinallyを追加して、finallyの中でsetIsLoading(false)を呼ぶように修正してください」と具体的に指示すると確実です。

パターン2: 条件分岐でローディング解除が抜ける

データの取得後に条件によって処理を分岐しているとき、一部のパスでローディング解除が漏れるケースがあります。

// 空配列のときにローディングが残るパターン
const fetchUserData = async (userId: string) => {
  setIsLoading(true);
  const data = await getUserData(userId);
 
  if (data && data.length > 0) {
    setUserList(data);
    setIsLoading(false); // データがある場合のみ実行される
  }
  // data が空配列のとき、setIsLoading(false) が呼ばれない
};

この場合も finally が解決策になりますが、よりシンプルな書き方として、ローディング解除を条件分岐の外に出す方法もあります。

// ローディング解除を条件外に移動した修正版
const fetchUserData = async (userId: string) => {
  setIsLoading(true);
  try {
    const data = await getUserData(userId);
    if (data && data.length > 0) {
      setUserList(data);
    } else {
      setEmptyMessage('ユーザーが見つかりませんでした');
    }
  } catch (error) {
    setError('データ取得に失敗しました');
  } finally {
    setIsLoading(false); // どの分岐でも確実に実行
  }
};

パターン3: useEffect の依存配列が原因でデータ再取得がループする

逆に「ローディングが終わったと思ったらすぐまた始まる」という症状の場合、useEffect の依存配列に問題があることがほとんどです。

// 無限ループになるパターン
useEffect(() => {
  fetchData();
  setLastFetched(new Date()); // これが再レンダリングを引き起こす
}, [lastFetched]); // lastFetched の変更で useEffect が再実行されてしまう

useEffect の依存配列に含まれている値が、useEffect の中で変更されると無限ループになります。

// 初回マウント時のみ実行する場合(空の依存配列)
useEffect(() => {
  fetchData();
}, []);
 
// 特定の値が変わったときだけ実行する場合
useEffect(() => {
  if (userId) {
    fetchData(userId);
  }
}, [userId]); // userId が変わったときだけ実行(userId 自体は fetchData の中で変更しない)

依存配列の中に「useEffect の中で更新している state」が含まれていないか確認してください。これがループの根本原因になっているケースが非常に多いです。

パターン4: レスポンスのデータ構造が想定と違う

APIはエラーなく200を返しているのに、画面にデータが表示されないケースです。取得したデータの構造が、コードが想定しているものと異なるときに起きます。

// APIレスポンスの実際の形: { success: true, result: { items: [...] } }
 
// 間違ったアクセス方法(undefined になる)
const data = await response.json();
setItems(data.items); // 本当は data.result.items
 
// 正しいアクセス方法
setItems(data.result.items);

この問題は console.log で確認するのが一番確実です。

const fetchData = async () => {
  setIsLoading(true);
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
 
    // 実際のデータ構造をログで確認する
    console.log('APIレスポンス:', JSON.stringify(data, null, 2));
 
    // 確認した構造に合わせてアクセスする
    setItems(data.result.items);
  } catch (error) {
    console.error(error);
  } finally {
    setIsLoading(false);
  }
};

Rork Companionアプリのコンソールログ、またはExpo開発サーバーのターミナルで console.log の出力を確認できます。レスポンスの中身を確認してからRorkに「data.result.items の配列をsetItemsに渡すように修正して」と伝えると正確に直してもらえます。

パターン5: コンポーネントのアンマウント後に setState が呼ばれる

画面を素早く切り替えたとき、「Can't perform a React state update on an unmounted component」という警告が出ることがあります。これは、コンポーネントが既に画面から消えた後に、非同期処理の結果がstateに反映されようとするときに起きます。

最近のReact/Expo環境では警告のみで動作への影響は限定的ですが、メモリリークの原因になるため対処しておく価値があります。

// クリーンアップ関数でアンマウント後の setState を防ぐ
useEffect(() => {
  let isMounted = true; // マウント状態を管理するフラグ
 
  const fetchData = async () => {
    setIsLoading(true);
    try {
      const data = await getData();
      if (isMounted) { // マウントされている場合のみ state を更新
        setItems(data);
      }
    } finally {
      if (isMounted) {
        setIsLoading(false);
      }
    }
  };
 
  fetchData();
 
  return () => {
    isMounted = false; // クリーンアップ時にフラグをオフにする
  };
}, []);

このパターンはやや複雑なので、Rorkに「useEffectの中でisMountedフラグを使ってアンマウント後のsetStateを防ぐように修正して」と伝えると対応してもらえます。

Rorkへの修正依頼の伝え方

ここまでのパターンを踏まえて、Rorkへの修正依頼で効果的なフレーズをまとめておきます。

エラー時のローディング解除漏れには「fetchData関数のtry/catchブロックにfinallyを追加して、setIsLoading(false)をfinallyの中に移動してください」と伝えます。

無限ループには「useEffectの依存配列を確認して、配列の中で変化している値を依存配列から外してください。初回のみ実行する場合は空配列にしてください」が有効です。

データが表示されない場合は「fetchData関数の中にconsole.log(JSON.stringify(data, null, 2))を追加して、APIレスポンスの実際の構造を確認できるようにしてください」と依頼します。

トラブルシューティングの順番

ローディングが終わらない・UIが更新されない問題に直面したら、この順番で確認してみてください。

まず setIsLoading(false) の呼び出し漏れを確認します。try の中だけに書いていないか、finally に移動することで解決しないかを試します。

次に console.log でAPIレスポンスの実際の中身を確認します。想定と異なるデータ構造が原因のケースは思いのほか多いです。

その後、useEffect の依存配列を確認します。配列の中に、その useEffect の中で変化する値が入っていないかチェックします。

それでも解決しない場合は、Rorkに「fetchData関数の全体を表示して」と依頼して、データフローを一から確認するのが近道です。

非同期処理のバグはAI生成コードでも人が書いたコードでも頻繁に起きます。「エラーではないのに動かない」という状況のほとんどは、ここで紹介したいずれかのパターンに当てはまります。ひとつひとつ確認していけば、必ず原因が見つかります。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-04-20
Rork 生成コードで useEffect が無限ループする原因と直し方
Rorkで生成したReact NativeコードのuseEffectが無限ループする原因を5パターン解説。依存配列の設定ミス・オブジェクト依存・setState競合など具体的なBefore/Afterコードで根本修正する方法をまとめています。
開発ツール2026-04-23
Rork で state を更新したのに画面が再描画されない原因と直し方
Rorkで生成したコードでsetStateしたのに画面が更新されない問題を5つの典型パターンで解説。配列のミューテート・古いクロージャ・Zustandの誤用など、再現性の高い原因を具体的なコード例で修正します。
開発ツール2026-04-22
FlatList が空白で何も映らない—Rork アプリのリスト崩れを切り分ける
Rork で生成したアプリの FlatList が真っ白で何も表示されないときに、どこから疑うべきかを順番にまとめました。data・flex・keyExtractor・親要素の高さを見直すだけで直ることも多いです。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →