React Native Academy · Ders

Gerçek Zamanlı Dinleyiciler ve Çevrimdışı Kalıcılık

onSnapshot ile bir Firestore koleksiyonuna abone olun, arayüzde canlı güncellemeler alın ve uygulamanın bağlantı olmadan önbelleğe alınmış verileri okuyabilmesi için çevrimdışı kalıcılığı etkinleştirin.

4. ders / 413 adım

Gerçek Zamanlı Dinleyiciler ve Çevrimdışı Kalıcılık, CoddyKit'te ücretsiz bir React Native Academy dersidir. Bu, 4 dersinin 4. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, React Native Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. React Native Academy kursu toplamda 4 dersten oluşur.

Gerçek Zamanlı Dinleyiciler ve Tek Seferlik Okumalar

Firestore verileri okumak için iki yöntem sunar: .get() ile tek seferlik okumalar ve .onSnapshot() ile gerçek zamanlı dinleyiciler. Gerçek zamanlı bir dinleyici, kalıcı bir bağlantıyı açık tutar ve ilk veri yüklemesi de dahil olmak üzere belge veya sorgu sonucu her değiştiğinde geri çağırma işlevinizi çalıştırır.

Verilere yalnızca bir kez ihtiyaç duyduğunuzda (örneğin uygulama başlatılırken kullanıcı ayarlarını yüklerken) .get() kullanın. Veriler sık değiştiğinde ve kullanıcı arayüzünün güncel kalması gerektiğinde (sohbet akışı veya canlı skor gibi) .onSnapshot() kullanın.

Tek Bir Belgeyi Dinleme

Belirli bir belgedeki değişiklikleri dinlemek için bir DocumentReference üzerinde .onSnapshot(callback) çağrısını yapın. Geri çağırma işlevi mevcut verilerle hemen çalışır ve belge her değiştiğinde yeniden çalışır.

.onSnapshot() bir abonelikten çıkma işlevi döndürür. Bunu bir değişkende saklayın ve bileşen kaldırıldığında dinleyiciyi durdurmak, böylece bellek sızıntılarını önlemek için useEffect temizleme işlevinde çağırın.

import { useEffect, useState } from 'react';
import firestore from '@react-native-firebase/firestore';

export function usePost(postId: string) {
  const [post, setPost] = useState(null);

  useEffect(() => {
    const unsubscribe = firestore()
      .collection('posts')
      .doc(postId)
      .onSnapshot((snapshot) => {
        if (snapshot.exists) {
          setPost({ id: snapshot.id, ...snapshot.data() });
        }
      });

    return unsubscribe; // Stop listener on unmount
  }, [postId]);

  return post;
}

Koleksiyon Sorgusunu Dinleme

Sonuç kümesindeki herhangi bir belge eklendiğinde, değiştirildiğinde veya kaldırıldığında canlı güncellemeleri almak için bir koleksiyon sorgusuna .onSnapshot() ekleyin. Geri çağırma işlevine iletilen QuerySnapshot, yalnızca değişenleri değil, eşleşen tüm belgeleri içerir.

Bu, bir belge her değiştiğinde snapshot.docs içinden listenin tamamını yeniden oluşturduğunuz anlamına gelir. Bu yaklaşım küçük ve orta boyutlu listeler için uygundur; ayrıca Firestore'un yerel önbelleği, önbelleğe alınmış veriler için ağ üzerinden gidiş-dönüş gerekmese bile işlemi hızlandırır.

useEffect(() => {
  const unsubscribe = firestore()
    .collection('messages')
    .where('roomId', '==', roomId)
    .orderBy('createdAt', 'desc')
    .limit(50)
    .onSnapshot((snapshot) => {
      const messages = snapshot.docs.map((doc) => ({
        id: doc.id,
        ...doc.data(),
      }));
      setMessages(messages);
    });

  return unsubscribe;
}, [roomId]);

Verimlilik için DocumentChange Kullanma

Her anlık görüntüde listenin tamamını yeniden oluşturmak yerine, tam olarak neyin eklendiğini, değiştirildiğini veya kaldırıldığını görmek için değişiklikler dizisini inceleyebilirsiniz. snapshot.docChanges() içindeki her girdinin bir type özelliği vardır: 'added', 'modified' veya 'removed'.

Bu yaklaşım, aynı anda yalnızca birkaç belgenin değiştiği büyük koleksiyonlarda daha verimlidir. Yerel durum dizisini tamamen değiştirmek yerine hedefli olarak güncellersiniz.

snapshot.docChanges().forEach((change) => {
  const item = { id: change.doc.id, ...change.doc.data() };

  if (change.type === 'added') {
    setItems((prev) => [item, ...prev]);
  } else if (change.type === 'modified') {
    setItems((prev) => prev.map((i) => (i.id === item.id ? item : i)));
  } else if (change.type === 'removed') {
    setItems((prev) => prev.filter((i) => i.id !== item.id));
  }
});

Dinleyici Hatalarını Yönetme

.onSnapshot() hata yönetimi için ikinci bir bağımsız değişken kabul eder. Dinleyici başarısız olduğunda — genellikle Firestore Güvenlik Kuralları kaynaklı bir izin hatası nedeniyle — hata geri çağırma işlevi bir FirebaseError ile çalışır.

Yaygın hata kodları arasında permission-denied (güvenlik kuralları okumayı engelledi) ve unavailable (çevrimdışı ve önbelleğe alınmış veri yok) bulunur. Sessizce başarısız olmak yerine alternatif bir kullanıcı arayüzü gösterebilmeniz için her zaman bir hata işleyicisi sağlayın.

const unsubscribe = firestore()
  .collection('private-data')
  .onSnapshot(
    (snapshot) => {
      setData(snapshot.docs.map((doc) => doc.data()));
    },
    (error) => {
      if (error.code === 'permission-denied') {
        setError('You do not have access to this data.');
      } else {
        setError('Could not load data. Check your connection.');
      }
    }
);

Firestore Çevrimdışı Kalıcılığı

React Native Firebase, mobil cihazlarda çevrimdışı kalıcılığı varsayılan olarak etkinleştirir. Bu, Firestore'un okuduğunuz veya dinlediğiniz tüm belgeleri cihazdaki yerel bir SQLite veritabanında önbelleğe aldığı anlamına gelir. Cihazda internet olmadığında okumalar, önbelleğe alınmış verileri hemen döndürür.

Bunu etkinleştirmek için herhangi bir yapılandırma yapmanız gerekmez — doğrudan çalışır. Önbellek boyutu sınırını (varsayılan 40 MB) denetleyebilirsiniz. Önbellek dolduğunda Firestore, en uzun süredir kullanılmayan belgeleri çıkarır. Ayrıca cacheSizeBytes: firestore.CACHE_SIZE_UNLIMITED ile sınırı sınırsız olarak ayarlayabilirsiniz.

import firestore from '@react-native-firebase/firestore';

// Configure cache size (call before any Firestore operations)
await firestore().settings({
  cacheSizeBytes: firestore.CACHE_SIZE_UNLIMITED,
  persistence: true, // enabled by default on mobile
});

// After this, all reads and writes work offline automatically

Önbellekten ve Sunucudan Okuma

Varsayılan olarak .get() önce sunucuyu dener, çevrimdışıysa önbelleğe geri döner. Bir kaynak seçeneği belirterek bu davranışı geçersiz kılabilirsiniz: { source: 'cache' } her zaman yerel önbellekten okur (anında, ağ bağlantısı olmadan), { source: 'server' } ise her zaman ağa bağlanır.

Yalnızca önbellekten okuma, mevcut verileri hemen görüntülerken yeni bir ağ isteğinin tamamlanmasını beklemek istediğiniz iyimser kullanıcı arayüzü düzenleri için kullanışlıdır. Belge önbellekte yoksa yalnızca önbellekten okuma işlemi hata verir.

// Always use cached data (instant, works offline)
const snapshot = await firestore()
  .collection('posts')
  .doc(postId)
  .get({ source: 'cache' });

// Always fetch from server (throws if offline)
const freshSnapshot = await firestore()
  .collection('posts')
  .doc(postId)
  .get({ source: 'server' });

// Default: server first, cache fallback
const snapshot2 = await firestore()
  .collection('posts')
  .doc(postId)
  .get();

Sorgu İmleçleriyle Sayfalama

Büyük koleksiyonlarda verileri sorgu imleçlerini kullanarak sayfalar halinde yükleyin: .startAfter(lastDocument). Yüklediğiniz son belgeye bir referans tutun ve sonraki sayfayı almak için bunu bir sonraki sorguya iletin. Buna imleç tabanlı sayfalama denir.

Son belge anlık görüntüsünü durumda saklayın. Kullanıcı listenin en altına kaydırdığında (FlatList'in onEndReached özelliği), son belgeden sonra başlayan yeni bir sorguyu tetikleyin. Yeni sonuçları mevcut listeye ekleyin.

const [posts, setPosts] = useState([]);
const [lastDoc, setLastDoc] = useState(null);
const [hasMore, setHasMore] = useState(true);

async function loadMore() {
  if (!hasMore) return;

  let query = firestore()
    .collection('posts')
    .orderBy('createdAt', 'desc')
    .limit(20);

  if (lastDoc) {
    query = query.startAfter(lastDoc);
  }

  const snapshot = await query.get();
  if (snapshot.empty) {
    setHasMore(false);
    return;
  }

  setLastDoc(snapshot.docs[snapshot.docs.length - 1]);
  const newPosts = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
  setPosts((prev) => [...prev, ...newPosts]);
}

Bileşik Dizinler

.where() ve .orderBy() işlemlerini farklı alanlarda birlikte kullandığınızda Firestore bir bileşik dizin gerektirir. Bu dizin olmadan sorgu, dizini otomatik olarak oluşturmanız için bir URL içeren failed-precondition hatası verir.

Bileşik dizinler firestore.indexes.json dosyasında yapılandırılır ve Firebase CLI ile dağıtılır. Geliştirme sırasında hata günlüğündeki URL'ye tıklamanız yeterlidir; Firebase dizini sizin için oluşturur. Üretim ortamında dizinleri her zaman kod üzerinden yönetin; böylece sürüm denetiminde tutulabilirler.

// This query requires a composite index on (userId ASC, createdAt DESC)
const snapshot = await firestore()
  .collection('posts')
  .where('userId', '==', userId)
  .orderBy('createdAt', 'desc')
  .get();

// If the index is missing, you see:
// FirebaseError: The query requires an index.
// https://console.firebase.google.com/project/.../firestore/indexes?...
// Click the link to auto-create the index

Ekran Değişikliğinde Dinleyicilerin Ayrılması

Gerçek zamanlı dinleyiciler bant genişliği ve pil tüketir. Kullanıcı bir ekrandan ayrıldığında bunları ayırmak için useEffect yerine React Navigation'ın useFocusEffect kancasını kullanın. useFocusEffect, ekran odaklandığında etkiyi çalıştırır ve ekran odağı kaybettiğinde temizleme işlemini gerçekleştirir.

Böylece yalnızca ilgili ekran görünürken etkin bir Firestore dinleyicisi tutarsınız; kullanıcı ekrandan ayrılıp geri döndüğünde dinleyici duraklatılır ve yeniden kurulur.

import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';

export function ChatScreen({ route }) {
  useFocusEffect(
    useCallback(() => {
      // Start listener when screen is focused
      const unsubscribe = firestore()
        .collection('messages')
        .onSnapshot(handleSnapshot);

      // Stop listener when screen loses focus
      return unsubscribe;
    }, [route.params.roomId])
  );
}

Yerel Durumla İyimser Güncellemeler

Akıcı bir kullanıcı deneyimi için değişiklikleri Firestore yazma işlemi tamamlanmadan hemen önce yerel duruma uygulayın; buna iyimser güncelleme denir. Değişikliği anında gösterin, ardından sunucu yanıtı geldiğinde gerçek zamanlı dinleyicinin değişikliği doğrulamasına veya düzeltmesine izin verin.

Örneğin kullanıcı bir gönderiyi beğendiğinde, yerel durumdaki beğeni sayısını hemen artırın. Sunucuya yazma işlemi başarısız olursa sayıyı azaltarak yerel durumu geri alın ve bir hata bildirimi gösterin. Bu yaklaşım, yavaş bağlantılarda hissedilen gecikmeyi önler.

async function toggleLike(postId: string, currentlyLiked: boolean) {
  // 1. Optimistically update local state
  setLiked(!currentlyLiked);
  setLikeCount((prev) => prev + (currentlyLiked ? -1 : 1));

  try {
    // 2. Persist to Firestore
    await firestore().collection('posts').doc(postId).update({
      likesCount: firestore.FieldValue.increment(currentlyLiked ? -1 : 1),
    });
  } catch (error) {
    // 3. Revert on failure
    setLiked(currentlyLiked);
    setLikeCount((prev) => prev + (currentlyLiked ? 1 : -1));
    Alert.alert('Error', 'Could not update like. Try again.');
  }
}

Hızlı Kontrol

Bu derste ele alınan React Native Mobil Geliştirme kavramlarını anlayıp anlamadığınızı test edin.

Ders Özeti

Bu derste şunları öğrendiniz: canlı veri güncellemeleri için gerçek zamanlı dinleyicilerin .onSnapshot() ile nasıl bağlanacağını, Firestore çevrimdışı kalıcılığının verileri çevrimdışı kullanım için otomatik olarak nasıl önbelleğe aldığını ve büyük koleksiyonların startAfter imleç tabanlı sayfalama kullanılarak nasıl sayfalanacağını. Sırada React Native uygulamalarının profilini çıkararak performans darboğazlarını ortadan kaldırmak var.

Başlamak ücretsiz

Yapay zeka eğitmeniyle JavaScript öğren — ücretsiz

Tarayıcında gerçek kod yaz ve çalıştır, 7/24 yapay zeka eğitmeninden anında yardım al; web'de ya da uygulamada kaldığın yerden devam et.

Kurslar
30
Dersler
120

Sıkça Sorulan Sorular

“Gerçek Zamanlı Dinleyiciler ve Çevrimdışı Kalıcılık” dersi ücretsiz mi?

Evet — “Gerçek Zamanlı Dinleyiciler ve Çevrimdışı Kalıcılık” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve React Native Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. React Native Academy kursu toplamda 4 dersten oluşur.

“Gerçek Zamanlı Dinleyiciler ve Çevrimdışı Kalıcılık” dersinde ne öğreneceğim?

onSnapshot ile bir Firestore koleksiyonuna abone olun, arayüzde canlı güncellemeler alın ve uygulamanın bağlantı olmadan önbelleğe alınmış verileri okuyabilmesi için çevrimdışı kalıcılığı etkinleştir… React Native Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

React Native Academy öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te React Native Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 4. dersidir.

“Gerçek Zamanlı Dinleyiciler ve Çevrimdışı Kalıcılık” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu React Native Academy dersinde kod yazıp çalıştırabilir miyim?

Evet. Her React Native Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. React Native Firebase'i Projenize Bağlama
  2. Firebase ile E-posta ve Telefon Kimlik Doğrulaması
  3. Firestore Belgelerini Okuma ve Yazma
  4. Gerçek Zamanlı Dinleyiciler ve Çevrimdışı Kalıcılık
← React Native Academy Sayfasına Dön