Heim > Web-Frontend > js-Tutorial > Hauptteil

Erstellen Sie Offline-First-Anwendungen mit React Native

DDD
Freigeben: 2024-09-19 06:32:02
Original
345 Leute haben es durchsucht

Building Offline-first Applications with React Native

在庫レベルを更新する小売業者、顧客データにアクセスする営業担当者、または断続的な接続中にメッセージを送信するユーザーによってアプリが使用されることを想像してください。これらすべての場合において、オフライン機能は、シームレスなユーザー エクスペリエンスとイライラするユーザー エクスペリエンスの違いを意味する可能性があります。ここでオフラインファーストの考え方が役立ちます。

オフラインファーストのアプローチにより、インターネットが利用できない場合でもアプリは機能し続けることが保証されます。 WhatsApp のようなアプリは、この概念を完璧に示しています。オフライン中にメッセージを送信すると、メッセージはローカルに保存され、接続が復元されると自動的に送信されます。このシームレスなエクスペリエンスは、ローカル ストレージを活用し、ネットワーク ステータスを監視することによって実現されます。データベース経由であってもデバイス メモリ経由であっても、アプリは引き続き機能し、接続が再び利用可能になったときに保存されたデータをサーバーと同期します。

この記事では、ローカル ストレージ、データベース同期、Expo API を使用して React Native アプリケーションにオフライン サポートを実装する方法を説明します。オフラインファーストのアプローチには次のような利点があります。

  1. ユーザー エクスペリエンスの向上: ユーザーがダウンタイムを経験する可能性が低くなり、全体的な満足度が向上します。
  2. データの一貫性: データはローカルに保存され、オンライン時に同期されるため、データの損失や破損が防止されます。
  3. エンゲージメントの向上: オフラインで動作するアプリは柔軟性が高く、特にインターネットが不安定な地域では、エンゲージメントと維持率が向上します。

Expo と React Native によるオフライン サポートのセットアップ

Expo は、多くのプラットフォーム固有の構成を抽象化し、機能の構築に集中できるため、React Native 開発に最適なフレームワークです。このセクションでは、Expo、ローカル ストレージ用の AsyncStorage、ネットワーク ステータス検出用の NetInfo を使用して、シンプルな React Native アプリにオフライン サポートを実装する方法を検討します。

1. プロジェクトをセットアップする

まず、Expo を利用した新しい React Native プロジェクトを作成することから始めましょう。

npx create-expo-app offline-first-app
cd offline-first-app
Nach dem Login kopieren

ステップ 2: 依存関係のインストール

この例では、2 つの主要なライブラリを使用します:

@react-native-async-storage/async-storage: このライブラリを使用すると、デバイスにデータを保存できます。
@react-native-community/netinfo: このライブラリは、ネットワーク ステータスを検出し、デバイスがオンラインかオフラインかを判断するのに役立ちます。

必要なパッケージをインストールします:

expo install @react-native-async-storage/async-storage @react-native-community/netinfo
Nach dem Login kopieren

ステップ 3: オフライン ロジックの実装

次に、オンライン時に API からデータを取得し、オフライン時に使用できるようにローカルに保存する単純なアプリケーションを構築します。まず、App.js で基本構造を設定します。

import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, Button, FlatList } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import NetInfo from '@react-native-community/netinfo';

const DATA_API = 'https://jsonplaceholder.typicode.com/posts';

export default function App() {
  const [data, setData] = useState([]);
  const [isOffline, setIsOffline] = useState(false);

  useEffect(() => {
    const loadData = async () => {
      // Check network status
      const netInfo = await NetInfo.fetch();
      setIsOffline(!netInfo.isConnected);

      if (netInfo.isConnected) {
        // Fetch data from API when online
        try {
          const response = await fetch(DATA_API);
          const result = await response.json();
          setData(result);

          // Cache the data for offline use
          await AsyncStorage.setItem('cachedData', JSON.stringify(result));
        } catch (error) {
          console.error('Failed to fetch data:', error);
        }
      } else {
        // Load data from AsyncStorage when offline
        try {
          const cachedData = await AsyncStorage.getItem('cachedData');
          if (cachedData) {
            setData(JSON.parse(cachedData));
          }
        } catch (error) {
          console.error('Failed to load data from cache:', error);
        }
      }
    };

    loadData();
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.header}>Offline-First App</Text>
      <Text>Status: {isOffline ? 'Offline' : 'Online'}</Text>

      <FlatList
        data={data}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => (
          <View style={styles.item}>
            <Text style={styles.title}>{item.title}</Text>
          </View>
        )}
      />

      <Button title="Reload" onPress={() => loadData()} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 50,
    paddingHorizontal: 20,
    backgroundColor: '#fff',
  },
  header: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  item: {
    backgroundColor: '#f9c2ff',
    padding: 20,
    marginVertical: 8,
  },
  title: {
    fontSize: 16,
  },
});
Nach dem Login kopieren

どのように機能しますか?

  1. ネットワーク ステータスの検出: NetInfo ライブラリを使用して、デバイスがオンラインかオフラインかを確認します。オンラインの場合、アプリは API からデータを取得してキャッシュします。デバイスがオフラインの場合、アプリはキャッシュされたデータを AsyncStorage から取得します。

  2. データ キャッシュ: AsyncStorage を使用すると、オフライン アクセスのために API から取得したデータを保存できます。これは、アクティブなインターネット接続がなくてもアプリを機能させるために不可欠です。

  3. データ同期: 接続が復元されると、アプリは API から新しいデータを取得してキャッシュを更新し、オンラインのユーザーが常に最新の情報を入手できるようにします。

高度なオフライン機能と重要な考慮事項

次のようなより高度な機能を統合することで、この基本機能を構築できます。

  1. 同期戦略: 一部のアプリでは、競合が発生する可能性がある高度な同期戦略が必要です (例: 2 人のユーザーが同じデータをオフラインで更新する)。 PouchDB や Firebase などのツールは、リアルタイムのデータ同期と競合解決の管理に役立ちます。

  2. データベース ソリューション: より複雑なアプリの場合、大規模なデータセットやより高度なクエリを処理するために Realm や SQLite などのローカル データベースを使用することもできます。

  3. 楽観的な更新: 一部のアプリ、特にソーシャル メディアなどのユーザー作成コンテンツを含むアプリでは、ユーザーがオフラインでデータを作成、更新、削除できるようにするのが一般的です。 UI で変更が即座に行われ、アプリがインターネットに再接続したときにサーバーと同期される、楽観的な更新を実装できます。


Handling Complex Syncing and Conflict Resolution

In an offline-first app, conflicts arise when multiple users update the same data while offline and their changes are later synced with the server once the app reconnects to the internet. Handling these conflicts is crucial to maintain data consistency and provide a smooth user experience.

There are different strategies for resolving such conflicts, including:

  1. Last Write Wins (LWW)
  2. Manual Conflict Resolution
  3. Operational Transformation (OT)

I have some examples here for you to check.

1. Last Write Wins (LWW)

In this strategy, the most recent change (based on a timestamp) is accepted as the final value when syncing data. It is simple and works well for many applications, but it may lead to data loss if multiple users edit the same data.

Imagine you are building a note-taking app, if two users edit the same note while offline, the user who syncs their changes last will overwrite the previous user’s changes.

Let’s assume we have a local storage system (using AsyncStorage) and a remote server.

import AsyncStorage from '@react-native-async-storage/async-storage';

// Simulate syncing the note data with the server
const syncNoteWithServer = async (localNote) => {
  try {
    // Fetch the server data
    const response = await fetch('https://api.example.com/note');
    const serverNote = await response.json();

    // Compare timestamps
    if (localNote.updatedAt > serverNote.updatedAt) {
      // Local version is newer, so overwrite the server
      await fetch('https://api.example.com/note', {
        method: 'PUT',
        body: JSON.stringify(localNote),
        headers: { 'Content-Type': 'application/json' },
      });
    } else {
      // Server version is newer, discard local changes
      await AsyncStorage.setItem('note', JSON.stringify(serverNote));
    }
  } catch (error) {
    console.error('Sync failed:', error);
  }
};

// Example usage
const localNote = {
  content: 'This is an updated note.',
  updatedAt: Date.now(), // Timestamp of the last local update
};

syncNoteWithServer(localNote);

Nach dem Login kopieren

In this example:
The app compares the updatedAt timestamp of the local note (stored offline) with the note stored on the server.
If the local note is newer, it overwrites the server version. Otherwise, it discards local changes and updates the app with the server version.

Pros:

  1. Simple to implement.
  2. Works well for non-critical data.

Cons:

  1. May lead to data loss (e.g., if both users made significant changes).
2. Manual Conflict Resolution

With manual conflict resolution, the user is prompted to resolve conflicts when multiple versions of the same data exist. This approach is more user-friendly in scenarios where every change is valuable and users need to decide which data to keep.

Here is a potential case: In a collaborative editing app, two users edit the same document while offline. Once both versions are synced, the user is prompted to choose which version to keep or merge.

import AsyncStorage from '@react-native-async-storage/async-storage';
import { Alert } from 'react-native';

// Simulate syncing the document with the server
const syncDocumentWithServer = async (localDoc) => {
  try {
    // Fetch the server data
    const response = await fetch('https://api.example.com/document');
    const serverDoc = await response.json();

    if (localDoc.updatedAt !== serverDoc.updatedAt) {
      // Conflict detected, ask the user to resolve it
      Alert.alert(
        'Document Conflict',
        'Both you and another user have edited this document. Choose which version to keep.',
        [
          {
            text: 'Keep Local',
            onPress: async () => {
              // Overwrite the server with local changes
              await fetch('https://api.example.com/document', {
                method: 'PUT',
                body: JSON.stringify(localDoc),
                headers: { 'Content-Type': 'application/json' },
              });
            },
          },
          {
            text: 'Keep Server',
            onPress: async () => {
              // Discard local changes and update the app with the server version
              await AsyncStorage.setItem('document', JSON.stringify(serverDoc));
            },
          },
        ],
      );
    } else {
      // No conflict, proceed with syncing
      await AsyncStorage.setItem('document', JSON.stringify(serverDoc));
    }
  } catch (error) {
    console.error('Sync failed:', error);
  }
};

// Example usage
const localDoc = {
  content: 'This is my latest edit.',
  updatedAt: Date.now(), // Timestamp of the last local update
};

syncDocumentWithServer(localDoc);
Nach dem Login kopieren

Here's what's happening
If the updatedAt timestamps differ between the local and server versions, the app alerts the user and asks them to choose which version to keep. The user can decide whether to keep the local or server version.

Pros:

  1. Ensures that no important data is lost.
  2. Suitable for collaborative apps where user input is valuable.

Cons:

  1. Requires user intervention, which can be disruptive.
  2. May confuse non-technical users.

3. Operational Transformation (OT)
Operational Transformation is a more advanced technique used in real-time collaboration apps like Google Docs. It automatically merges conflicting changes by transforming operations in a way that preserves both sets of edits. OT allows multiple users to work on the same document simultaneously, and their changes are merged intelligently.

In a document editor app, two users edit different parts of a document. OT ensures that both sets of edits are applied without overwriting each other.

This implementation is a bit complex and require specialized libraries, such as ShareDB or Yjs. Here’s a basic pseudocode example of how OT works:

// Example of transforming two concurrent operations
const operation1 = { type: 'insert', position: 5, value: 'Hello' }; // User 1 adds 'Hello' at position 5
const operation2 = { type: 'insert', position: 3, value: 'World' }; // User 2 adds 'World' at position 3

const transformOperations = (op1, op2) => {
  // If both operations modify different positions, no conflict
  if (op1.position !== op2.position) return [op1, op2];

  // If operations conflict, adjust positions accordingly
  if (op1.position > op2.position) op1.position += op2.value.length;
  else op2.position += op1.value.length;

  return [op1, op2];
};

// Transform the operations to avoid conflicts
const [transformedOp1, transformedOp2] = transformOperations(operation1, operation2);
Nach dem Login kopieren

The positions of the two conflicting operations are adjusted so that they can both be applied without overwriting each other.

Pros:

  1. Ideal for real-time collaboration.
  2. Automatically resolves conflicts without user intervention.

Cons:

  1. Complex to implement.
  2. Requires specialized algorithms and libraries.

Conclusion
Each conflict resolution strategy comes with its trade-offs. For simpler apps, Last Write Wins may suffice. However, for collaborative apps where user data is crucial, Manual Conflict Resolution or more advanced techniques like Operational Transformation might be necessary. Choosing the right strategy depends on the complexity of your app and the importance of the data being modified.


I plan to create a series of articles that dive deeper into the following key topics:

Optimistic UI Updates – We'll explore how to immediately reflect changes made while offline in the UI, giving users the impression that their actions were successful. This approach greatly improves the user experience.

Verwendung von Servicemitarbeitern für webbasierte Apps – Wenn Sie Ihre App über React Native Web im Web bereitstellen, erkläre ich, wie Servicemitarbeiter Offline-Caching und Hintergrundsynchronisierung für Progressive Web aktivieren können Apps (PWAs). Dadurch wird sichergestellt, dass Benutzer auch dann auf Ressourcen und Daten zugreifen können, wenn sie offline sind.

Reale Anwendungsfälle von Offline-First-Apps – Ich werde mir genauer ansehen, wie Apps wie Google Maps, Slack, Trello und Notion mit Offline-Szenarien umgehen. Durch das Studium dieser Beispiele erhalten Sie ein besseres Verständnis für die praktischen Anwendungen von Offline-First-Techniken.

Offline-Funktionen testen – Wir besprechen die Bedeutung des Testens von Offline-Funktionen und überprüfen Tools wie React Native Debugger, Expo-Tools und den Network Link Conditioner (für iOS), um Netzwerkunterbrechungen zu simulieren. Ich zeige Ihnen auch, wie Sie mithilfe von Bibliotheken wie Jest und React Native Testing Library Tests schreiben, um sicherzustellen, dass sich Ihre App unter Offline-Bedingungen korrekt verhält.

Überlegungen zu Leistung und Speicher – Bei der Leistung geht es nicht nur um Geschwindigkeit; Es geht auch um die Benutzererfahrung. Ich bespreche Strategien zur Leistungsoptimierung durch die Reduzierung zwischengespeicherter Daten und die Implementierung von Datenablaufrichtlinien, um eine Überlastung des lokalen Speichers zu vermeiden.

Bleibt dran, Entwickler.


Vielen Dank, dass Sie den Artikel vollständig gelesen haben. Es macht mir wirklich Spaß, meine Erkenntnisse zu dokumentieren und zu teilen. Ich habe vor, weitere Inhalte zu erstellen, darunter auch Video-Tutorials, die ich auf Instagram und TikTok teilen werde. Wenn Sie neu hier sind, ich bin Zidane Gimiga, ein Softwareentwickler mit einer Leidenschaft für die Optimierung von Benutzererlebnissen. Da Technologie immer stärker in unser Leben integriert wird, ist es wichtig, sie für alle so einfach und zugänglich wie möglich zu machen. Lassen Sie uns weiterhin auf bessere, benutzerfreundliche Lösungen drängen.

Oh, ich bin auf Github

Das obige ist der detaillierte Inhalt vonErstellen Sie Offline-First-Anwendungen mit React Native. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!