◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SONNET 4.5 — Claude API での提供終了は 11/30、残り26日。API 連携を足したアプリは移行先の確認を●SDK 58 — React Native 0.88 に更新されました。Android のリリース版で R8 が既定オンになり、iOS は SceneDelegate を生成します●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SONNET 4.5 — Claude API での提供終了は 11/30、残り26日。API 連携を足したアプリは移行先の確認を●SDK 58 — React Native 0.88 に更新されました。Android のリリース版で R8 が既定オンになり、iOS は SceneDelegate を生成します●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/アプリ開発
◇ アプリ開発/2026-06-21上級

壁紙グリッドに戻ると一番上に戻ってしまう — スクロール位置を「戻る」とプロセス復帰の両方で保つ

壁紙アプリの一覧で詳細を見て戻ると先頭に飛んでしまう問題を、ナビゲーションの戻りとプロセス復帰の二つに分けて設計し直しました。getItemLayout と保存タイミング、復元時のちらつきまで含めた実装メモです。

Rork612FlatList10スクロール位置状態復元expo14

✦ プレミアム記事

スワイプして見ていた壁紙の一覧で、気になった一枚をタップして拡大し、戻るボタンを押す。すると一覧は一番上に戻っていて、さっきまで見ていた行を指でもう一度探し直す。個人開発で壁紙アプリを何本か運用していると、このひと手間がどれだけ離脱を生むかが、利用データからじわじわ見えてきます。

私自身、最初は「戻るときにスクロール位置を覚えておけばいい」とだけ考えていました。ところが実装してみると、位置がずれる場面は二つあって、それぞれ原因がまったく違うことに気づきます。一つは画面を行き来する「戻る」での消失。もう一つは、OS がメモリ不足でアプリを裏で落としたあと、再び開いたときの消失です。混ぜて考えると、どちらも中途半端にしか直りません。

二つの「位置ずれ」を最初に分ける

先に切り分けておきます。ここを曖昧にしたまま保存処理を足すと、不要な永続化でスクロールがかえってカクつきます。

場面何が失われるか必要な対処
詳細へ進んで戻る(スタック内)原則は失われない。タブ切替や条件付きアンマウントで失われる画面をアンマウントしない構成にする
アプリを閉じて開き直す(プロセス継続)失われない(メモリ上の状態が残る)対処不要
OS が裏で強制終了 → 再起動メモリ上の状態がすべて消えるオフセットを端末に保存して復元する

つまり、永続化が本当に要るのは三つ目だけです。一つ目はナビゲーションの構成の問題で、保存とは無関係に解けます。ここを取り違えると、毎フレーム位置をディスクに書きに行くような重い実装になりがちです。

「戻る」で先頭に飛ぶのは、たいてい画面が消えているから

ネイティブスタックのナビゲーションでは、詳細画面を上に重ねても一覧画面はマウントされたまま背後に残ります。FlatList の内部状態もそのまま残るので、戻れば同じ位置に表示されるのが本来の挙動です。

それでも先頭に飛ぶときは、一覧画面がどこかでアンマウントされています。よくあるのは次の二つです。

ひとつは、タブやセグメントの切り替えを condition ? <List/> : <Other/> のような条件付きレンダリングで実装している場合。切り替えるたびに <List/> が丸ごと作り直され、スクロール位置はゼロに戻ります。

もうひとつは、データ取得中に if (loading) return <Spinner/> で一覧そのものを差し替えている場合。再取得のたびにリストが一度消えて、再描画で先頭に戻ります。

対処はシンプルで、一覧を消さないことです。

// ❌ 切り替えのたびにListが作り直される
function Screen({ tab }: { tab: 'all' | 'favorites' }) {
  return tab === 'all' ? <WallpaperList /> : <FavoriteList />;
}
 
// ✅ 両方マウントしたまま、表示だけ切り替える
function Screen({ tab }: { tab: 'all' | 'favorites' }) {
  return (
    <>
      <View style={{ flex: 1, display: tab === 'all' ? 'flex' : 'none' }}>
        <WallpaperList />
      </View>
      <View style={{ flex: 1, display: tab === 'favorites' ? 'flex' : 'none' }}>
        <FavoriteList />
      </View>
    </>
  );
}

display: 'none' で隠す方法なら、リストはマウントされ続けてスクロール位置を保ちます。タブが二つ三つなら、この素朴なやり方で十分実用になります。読み込み中のスピナーも、リスト全体を差し替えるのではなく、上に重ねるか、リスト内のヘッダーとして出すと位置が保たれます。

ここまでで「戻る」での位置ずれの大半は消えます。残りはプロセス復帰の方です。

✦

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

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦「戻る」での位置ずれとプロセス復帰での位置ずれは原因が別物で、対処も別という切り分け
✦getItemLayout で行インデックスを計算し、初回描画前に復元してちらつきを消す実装
✦保存を onScroll で間引き、バックグラウンド遷移で確定保存する負荷の低いタイミング設計
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

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

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

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

関連記事

◇ アプリ開発2026-10-28
「Project is incompatible with this version of Expo Go」と出たとき、プロジェクトを疑う前に見る三か所
Expo Go で「このプロジェクトには新しい Expo Go が必要です」と表示されたとき、node_modules を消す前に確かめたい三つの照合先。プロジェクトの SDK、アプリの対応 SDK、そして Android で実際に起きた Expo Go 56.0.1 の取り違えまで、順に絞り込む手順を書き残します。
◇ アプリ開発2026-10-24
EAS の Android ビルドで Could not find method exec() と出たら、自分のコードではなく Script 行のパスを先に読みます
SDK を上げた直後の EAS Android ビルドが、node_modules の中の .gradle スクリプトで Could not find method exec() と止まる。Sentry の事例を手がかりに、ログの Script 行から原因パッケージを特定して版を確かめる順番を、小さなシェルスクリプトでまとめました。
◇ アプリ開発2026-10-22
Stripe を入れた Expo SDK 58 で pod install が止まる — precompiled modules を切るか、直るまで待つかの判断
useFrameworks を dynamic にしているのに Stripe が static と言われて SDK 58 の iOS ビルドが止まる件を、報告された条件と三つの逃げ道、切り分けの点検スクリプトで整理します。