npm package: @smart-hook/react-smart-hook-firebase
Description: firebase hooks with high performance
Latest version: 0.0.20

# Overview

React hook for Firebase based on `react-hook-retention-cache`.

## Difference from react-firebase-hook and plain firebase.

Firebase has an original excellent caching mechanism. In an example of firestore, if you have effective onSnapshot subscription, you can use a cached value via `getDocFromCache`, `getDoc` or other `onSnapshot` subscriptions. But this mechanism has a severe limitation.

1. You can only get value asynchronously, even if it has been cached. (`getDocFromCache` can only return Promise)
2. If a React component subscribes firestore resource and unsubscribe in `unmount`, a cache of a value is also released. And next page cannot use cache. This behaviour cannot be changes at least you use react-firebase-react-hook.

This package resolve these problems with smart caching mechanism privided by `react-hook-retention-cache`.

## How to handle data model.

In Firestore + TypeScript project, how to handle data model of doc / collection is troublesome problem.
One of a smart strategy is to use a model dependent hooks. 
This strategy is also compatible with caching.

All functions of this package has form of `create***Hook(callback)` and generate model dependent hooks. This is very suitable for a modern TypeScript project.

## Destructive Change in v0.0.17
- The sequence of parameters passed to createIdTokenHook has been changed.

## Destructive Change in v0.0.15
- Hooks generated by createDocumentHook / createQueryHook has become to accept params of undefined / null. 
- By this reason, they has become not to accept refGenerator with no params (i.e. () => D ).
- To cover this old use case, new createFixedQueryHook / createFixedDocumentHook has been introduced.

## Example

```ts
// Doc
export const useClientDoc = createDocumentHook((id: string) =>
  doc(clientCollection, id)
);
/*  (params: string | undefined) => { 
    snapshot?: DocumentSnapshot<D>;
    error?: FirestoreError;
    data?: D;
    ref?: DocumentReference<D>;
    loading?: boolean;
} */

export const useDefaultClientDoc = createFixedQueryHook(() =>
  doc(clientCollection, "default")
);
/*  () => { 
    snapshot?: DocumentSnapshot<D>;
    error?: FirestoreError;
    data?: D;
    ref: DocumentReference<D>;
    loading?: boolean;
} */

// Query
export const useCouponList = createFixedQueryHook(() => {
  return query(userCollection, orderBy("createdAt", "desc").withConverter(withIdConverter<D>()));
});
/* () =>{
    snapshot?: QuerySnapshot<D>;
    error?: FirestoreError;
    list?: D[];
    ref: Query<D>;
    loading?: boolean;
} */

const useIdTokenResult = createIdTokenHook(auth, (claims, user) => {
  return {
    role: claims?.role as string | undefined,
    uid: claims?.sub as string | undefined,
    email: claims?.email as string | undefined,
  };
});
/* () => {
    error?: Error;
    data?: {
      role: string | undefined;
      uid: string | undefined;
      email: string | undefined;
  };
} */
```

# API

## firestore doc
```ts
declare type DocumentResult<D> = {
    snapshot?: DocumentSnapshot<D>;
    error?: FirestoreError;
    data?: D;
    ref: DocumentReference<D>;
    loading?: boolean;
};
declare type UseDocumentOption = {
    withData?: boolean;
    retentionTime?: number | undefined;
};
declare function createDocumentHook<D, P>(refGenerator: (params: P) => DocumentReference<D>, { withData, retentionTime }?: UseDocumentOption): (params: P | null | undefined) => emptyObject | DocumentResult<D>;
declare function createFixedDocumentHook<D>(refGenerator: () => DocumentReference<D>, { withData, retentionTime }?: UseDocumentOption): () => emptyObject | DocumentResult<D>;
declare const createDocumentRefHook: ({ withData, retentionTime }?: UseDocumentOption) => <D>(params: DocumentReference<D>) => DocumentResult<D>;
```

## firestore query (including collection)
```ts
declare type QueryResult<D> = {
    snapshot?: QuerySnapshot<D>;
    error?: FirestoreError;
    list?: D[];
    ref: Query<D>;
    loading?: boolean;
};
declare type UseQueryOption = {
    withData?: boolean;
    retentionTime?: number | undefined;
};
declare const createQueryHook: <D, P>(refGenerator: (params: P) => Query<D>, { withData, retentionTime }?: UseQueryOption) => (params: P | null | undefined) => emptyObject | QueryResult<D>;
declare const createFixedQueryHook: <D>(refGenerator: () => Query<D>, { withData, retentionTime }?: UseQueryOption) => () => emptyObject | QueryResult<D>;
declare const createQueryRefHook: (options?: UseQueryOption) => <D>(params: Query<D>) => QueryResult<D>;
```


## firebase authentication 
```ts
declare type IdTokenResult<D> = {
    error?: Error;
    data?: D;
};
declare const createIdTokenHook: <D>(auth: Auth, projector: (user?: User | undefined, claims?: ParsedToken | undefined, ) => D, { retentionTime }?: {
    retentionTime: number | undefined;
}) => () => IdTokenResult<D>;
```

```ts
declare type AuthResult<D> = {
    error?: Error;
    data?: D;
};
declare const createAuthHook: <D>(auth: Auth, projector: (user?: User | undefined) => D, { retentionTime }?: {
    retentionTime: number | undefined;
}) => () => AuthResult<D>;
```