reactjs - Firebase 9 med React Typescript: Hvordan endrer jeg typene til querySnapshot?


Når jeg prøver å lagre et øyeblikksbilde av spørringen min fra Firestore, returnerer det som q: Query<DocumentData>, og spørringsøyeblikksbildet mitt er querySnap: QuerySnapshot<DocumentData>.

DocumentData-typen er: [field: string]: any; Og jeg kan ikke gå gjennom den uten å få noen feil.

min effektkode

useEffect(() => {
    const fetchListings = async () => {
      try {
        // Get reference
        const listingsRef = collection(db, "listings");

        // Create a query
        const q = query(
          listingsRef,
          where("type", "==", params.categoryName),
          orderBy("timestamp", "desc"),
          limit(10)
        );

        // Execute query
        const querySnap = await getDocs(q);

        let listings: DocumentData | [] = [];

        querySnap.forEach((doc) => {
          return listings.push({ id: doc.id, data: doc.data() });
        });

        setState((prevState) => ({ ...prevState, listings, loading: false }));
      } catch (error) {
        toast.error("Something Went Wront");
      }
    };

    if (mount.current) {
      fetchListings();
    }

    return () => {
      mount.current = false;
    };
  }, [params.categoryName]);

Vet noen hvordan jeg kan sette oppførselstypen min riktig?

Oppføringstypen fra Firestore bør være:

type GeoLocationType = {
  _lat: number;
  _long: number;
  latitude: number;
  longitude: number;
};

export type ListingsDataType = {
  bathrooms: number;
  bedrooms: number;
  discountedPrice: number;
  furnished: boolean;
  geolocation: GeoLocationType;
  imageUrls: string[];
  location: string;
  name: string;
  offer: boolean;
  parking: boolean;
  regularPrice: number;
  timestamp: { seconds: number; nanoseconds: number };
  type: string;
  userRef: string;
};

Svar #1

Som du har oppdaget, kan du enkelt tvinge typen til referansen:

const listingsRef = collection(db, "listings") as CollectionReference<ListingsDataType>;

Men selv om dette fungerer, kan du støte på fremtidige problemer der du møter en uventet type eller har andre nestede data som ikke oversettes så godt til Firestore.

Dette er hvor den tiltenkte bruken av generisk type kommer inn, der du i stedet bruker et FirestoreDataConverter-objekt på referansen for å konvertere mellom DocumentData og typen du velger. Dette brukes normalt med klassebaserte typer.

import { collection, GeoPoint, Timestamp } from "firebase/firestore";

interface ListingsModel {
  bathrooms: number;
  bedrooms: number;
  discountedPrice: number;
  furnished: boolean;
  geolocation: GeoPoint; // <-- note use of true GeoPoint class
  imageUrls: string[];
  location: string;
  name: string;
  offer: boolean;
  parking: boolean;
  regularPrice: number;
  timestamp: Date; // we can convert the Timestamp to a Date
  type: string;
  userRef: string; // using a converter, you could expand this into an actual DocumentReference if you want
} 

const listingsDataConverter: FirestoreDataConverter<ListingsModel> = {
  // change our model to how it is stored in Firestore
  toFirestore(model) {
    // in this case, we don't need to change anything and can
    // let Firestore handle it.
    const data = { ...model } as DocumentData; // take a shallow mutable copy

    // But for the sake of an example, this is where you would build any
    // values to query against that can't be handled by a Firestore index.
    // Upon being written to the database, you could automatically
    // calculate a `discountPercent` field to query against. (like "what
    // products have a 10% discount or more?")
    if (data.offer) {
      data.discountPercent = Math.round(100 - (model.discountedPrice * 100 / model.regularPrice))) / 100; // % accurate to 2 decimal places
    } else {
      data.discountPercent = 0; // no discount
    }
    return data;
  },

  // change Firestore data to our model - this method will be skipped
  // for non-existant data, so checking if it exists first is not needed
  fromFirestore(snapshot, options) { 
    const data = snapshot.data(options)!; // DocumentData
    // because ListingsModel is not a class, we can mutate data to match it
    // and then tell typescript that it is now to be treated as ListingsModel.
    // You could also specify default values for missing fields here.
    data.timestamp = (data.timestamp as Timestamp).toDate(); // note: JS dates are only precise to milliseconds
    // remove the discountPercent field stored in Firestore that isn't part
    // of the local model
    delete data.discountPercent;
    return data as ListingsModel;
  }
}

const listingsRef = collection(db, "listings")
  .withConverter(listingsDataConverter); // after calling this, the type will now be CollectionReference<ListingsModel>

Dette er dokumentert på følgende steder:

  • Egendefinerte objekter med Firestore
  • FirestoreDataConverter
  • CollectionReference#withConverter(converter)

Svar #2

Problem løst: alt jeg måtte gjøre var:

const listingsRef = collection(
          db,
          "listings"
        ) as CollectionReference<ListingsDataType>;

Relaterte artikler