島根県安来市のフリーランスエンジニア_プログラマー画像1

React NativeでJSONキャッシュ問題を解決|外部データ更新が反映されない原因と対処法

🔴 問題: サーバー上のJSONファイルを更新しても、React Nativeアプリに反映されない

React Nativeアプリで外部JSONファイルからデータを取得する際、サーバー側でファイルを更新しても、アプリ側では古いデータが表示され続ける問題に遭遇しました。RSSフィードは正常に更新されるのに、なぜJSONファイルだけ更新されないのか?その原因と解決方法を共有します。

📋 状況整理

正常に動作していたもの

  • ✅ RSSフィード(XML形式) → サーバー更新が即座に反映

更新されなかったもの

  • ❌ 外部JSONファイル → サーバー更新しても反映されない

🔍 原因分析

1. データの取得方法に問題があった

当初、データはアプリ内に静的にインポートされていました:

// ❌ 問題のあるコード
import { dataList } from '../data/localfile';

// このデータはビルド時に埋め込まれるため、
// アプリを再ビルドしない限り更新されない

2. RSSとJSONの違い

比較項目 RSS(XML) JSON(静的ファイル)
キャッシュ制御 配信元が適切に設定 サーバー設定次第
更新頻度 配信元が自動更新 手動更新
ブラウザの扱い 動的コンテンツ 静的ファイルとして強くキャッシュ
結論: JSONファイルは静的ファイルとして強くキャッシュされていたため、更新されなかった

✅ 解決策

ステップ1: 動的なデータ取得に変更

// ✅ 改善後のコード
const [dataList, setDataList] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
  const fetchData = async () => {
    try {
      const response = await fetch('https://example.com/data.json');
      const data = await response.json();
      setDataList(data);
    } catch (error) {
      console.error('Error:', error);
    } finally {
      setLoading(false);
    }
  };
  
  fetchData();
}, []);

ステップ2: HTTPキャッシュ対策(重要!)

しかし、これだけではまだキャッシュ問題は解決しません。以下の2つの対策が必要です:

① URLにタイムスタンプを追加(キャッシュバスト)

const timestamp = new Date().getTime();
const response = await fetch(
  `https://example.com/data.json?_=${timestamp}`
);
仕組み: タイムスタンプを付けることで、毎回「異なるURL」として認識され、キャッシュが回避される

② HTTPヘッダーでキャッシュ制御

const response = await fetch(url, {
  method: 'GET',
  headers: {
    'Cache-Control': 'no-cache',  // プロキシキャッシュを無効化
    'Pragma': 'no-cache'          // HTTP/1.0互換
  }
});

完成版のコード

const [dataList, setDataList] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
  const fetchData = async () => {
    try {
      // キャッシュバスト用のタイムスタンプ
      const timestamp = new Date().getTime();
      
      const response = await fetch(
        `https://example.com/data.json?_=${timestamp}`,
        {
          method: 'GET',
          headers: {
            'Cache-Control': 'no-cache',
            'Pragma': 'no-cache'
          }
        }
      );
      
      if (!response.ok) {
        throw new Error('データ取得失敗');
      }
      
      const data = await response.json();
      setDataList(data);
      setLoading(false);
    } catch (err) {
      console.error('Error:', err);
      setError(err.message);
      setLoading(false);
    }
  };

  fetchData();
}, []);

🎯 技術的に重要なポイント

1. importとfetchの根本的な違い

方式 タイミング 更新方法
import ビルド時 アプリ再ビルドが必要
fetch 実行時 サーバー更新のみでOK

2. キャッシュバストの注意点

デメリット: 毎回新しいリクエストになるため、サーバー負荷が増加する可能性がある

代替案: 更新頻度が低い場合は、時間単位やバージョン番号でキャッシュ管理

// 1時間ごとにキャッシュを更新
const timestamp = Math.floor(Date.now() / (1000 * 60 * 60));

// または、バージョン番号で管理
const version = "v1.0";
fetch(`url?v=${version}`);

3. React Nativeでの非同期データ取得のベストプラクティス

  • 3つの状態を管理: データ、ローディング、エラー
  • try-catch-finally構文: 必ずfinallyでローディング状態を解除
  • 空の依存配列: useEffect第2引数に[]を渡し、マウント時のみ実行

📊 結果

✅ 成功:
  • サーバーのJSONファイルを更新するだけで、アプリに即座に反映
  • アプリの再ビルド・再配布が不要に
  • 運用コストが大幅に削減

🔧 サーバー側での追加対応(推奨)

アプリ側だけでなく、サーバー側でもキャッシュ制御を設定すると、より確実です:

Apache (.htaccess)

<FilesMatch "\.(json)$">
  Header set Cache-Control "no-cache, no-store, must-revalidate"
  Header set Pragma "no-cache"
  Header set Expires 0
</FilesMatch>

Nginx

location ~* \.json$ {
  add_header Cache-Control "no-cache, no-store, must-revalidate";
  add_header Pragma "no-cache";
  add_header Expires 0;
}

📝 まとめ

  1. 静的インポートは避ける: ビルド時に埋め込まれるため、更新にはアプリ再配布が必要
  2. 動的取得にはキャッシュ対策が必須: タイムスタンプ追加とヘッダー設定の両方を実施
  3. サーバー側でも対応: より確実にキャッシュを制御できる
  4. 更新頻度に応じた最適化: 負荷とリアルタイム性のバランスを考慮

この対応により、JSONファイルの更新がリアルタイムでアプリに反映されるようになり、運用が大幅に改善されました。同じ問題に直面している方の参考になれば幸いです。

自作WordPressプラグインが海外で紹介されました | 個人開発者の実績

サイト更新をLINEに自動通知する方法|手動チェックをゼロにした仕組みを解説