Search code examples
reactjstypescriptfirebasegoogle-cloud-firestorereact-firebase-hooks

How to use useDocumentData from React Firebase Hook with typed data?


I'm trying to use useDocumentData of the react fire hook library.

I'm struggling a bit on how to retrieve the converted data.

So far, I've this:

export const useProfile = () => {
  const [user] = useAuthState(auth);
  const docRef = doc(db, "profiles", user!.uid);
  useDocumentData<UserProfile>(docRef, {
    snapshotListenOptions: {
      includeMetadataChanges: true,
    },
  });
};
export interface UserProfile {
  propOne: string;
  propTwo: string;
}

My issue, is that it says, on the first parameter of useDocumentData:

Argument of type 'DocumentReference<DocumentData, DocumentData>' is not assignable to parameter of type 'DocumentReference<UserProfile, DocumentData>'.
  Types of property 'converter' are incompatible.
    Type 'FirestoreDataConverter<DocumentData, DocumentData> | null' is not assignable to type 'FirestoreDataConverter<UserProfile, DocumentData> | null'.
      Type 'FirestoreDataConverter<DocumentData, DocumentData>' is not assignable to type 'FirestoreDataConverter<UserProfile, DocumentData>'.
        The types returned by 'fromFirestore(...)' are incompatible between these types.
          Type 'DocumentData' is not assignable to type 'UserProfile'.ts(2345)

I'm totally aware that I can make a converter to transform data if needed, but in my case, no transformation are required, I've only fields that maps directly to my profile, so to my understanding, it should not be required.

How can I call useDocumentData without having this error? Especially if my object doesn't require a conversion?


Solution

  • Because type of docRef is DocumentReference<DocumentData>, it's not possible to pass it to useDocumentData<UserProfile> since type of the first argument is DocumentReference<UserProfile>.

    So all you need to do is convert docRef to DocumentReference<UserProfile>.

    1. type casting

    const docRef = doc(db, "profiles", user!.uid) as DocumentReference<UserProfile>;
    

    Because your interface is simple, there's little possibility of error, but sometimes it's better to go safer.

    2. FirestoreDataConverter

    You can create custom object that converts from firebase to your own data and reverse.

    const converter = {
      toFirestore(doc: UserProfile): DocumentData {
        return doc;
      },
      fromFirestore(snapshot: QueryDocumentSnapshot): UserProfile {
        return snapshot.data() as UserProfile;
      }
    };
    
    // ...
    
    const docRef = doc(db, "profiles", user!.uid).withConverter(converter);
    

    This way, you can customize the way firebase convert it's data.

    For more information, check this URL: https://firebase.google.com/docs/reference/js/firestore_.firestoredataconverter