Skip to content
Next Next commit
Add hooks to get firestore document data once
  • Loading branch information
davepwsmith committed Feb 9, 2020
commit 632d876e6cf1a37f7e503c97eea1d234d8ccb785
48 changes: 47 additions & 1 deletion reactfire/firestore/firestore.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
useFirestoreCollection,
FirebaseAppProvider,
useFirestoreCollectionData,
useFirestoreDocData
useFirestoreDocData,
useFirestoreDocDataOnce
} from '..';
import { firestore } from 'firebase/app';

Expand Down Expand Up @@ -104,6 +105,51 @@ describe('Firestore', () => {
});
});

describe('useFirestoreDocDataOnce', () => {
it('does not update on database changes [TEST REQUIRES EMULATOR]', async () => {
const mockData1 = { a: 'hello' };
const mockData2 = { a: 'goodbye' };

const ref = app
.firestore()
.collection('testDoc')
// 'readSuccess' is set to the data-testid={data.id} attribute
.doc('readSuccess');

await ref.set(mockData1);

const ReadFirestoreDoc = () => {
const dataOnce = useFirestoreDocDataOnce<any>(ref, { idField: 'id' });
const data = useFirestoreDocData<any>(ref, { idField: 'id' });

return (
<>
<h1 data-testid="once">{dataOnce.a}</h1>{' '}
<h1 data-testid="subscribe">{data.a}</h1>
</>
);
};
const { getByTestId } = render(
<FirebaseAppProvider firebase={app}>
<React.Suspense fallback={<h1 data-testid="fallback">Fallback</h1>}>
<ReadFirestoreDoc />
</React.Suspense>
</FirebaseAppProvider>
);

await waitForElement(() => getByTestId('once'));
await waitForElement(() => getByTestId('subscribe'));

expect(getByTestId('once')).toContainHTML(mockData1.a);
expect(getByTestId('subscribe')).toContainHTML(mockData1.a);

await act(() => ref.set(mockData2));

expect(getByTestId('once')).toContainHTML(mockData1.a);
expect(getByTestId('subscribe')).toContainHTML(mockData2.a);
});
});

describe('useFirestoreCollection', () => {
it('can get a Firestore collection [TEST REQUIRES EMULATOR]', async () => {
const mockData1 = { a: 'hello' };
Expand Down
35 changes: 35 additions & 0 deletions reactfire/firestore/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
checkStartWithValue
} from '..';
import { preloadObservable } from '../useObservable';
import { first } from 'rxjs/operators';

// starts a request for a firestore doc.
// imports the firestore SDK automatically
Expand Down Expand Up @@ -49,6 +50,23 @@ export function useFirestoreDoc<T = unknown>(
);
}

/**
* Suscribe to Firestore Document changes
*
* @param ref - Reference to the document you want to listen to
* @param options
*/
export function useFirestoreDocOnce<T = unknown>(
ref: firestore.DocumentReference,
options?: ReactFireOptions<T>
): T extends {} ? T : firestore.DocumentSnapshot {
return useObservable(
doc(ref).pipe(first()),
'firestore doconce: ' + ref.path,
options ? options.startWithValue : undefined
);
}

/**
* Suscribe to Firestore Document changes
*
Expand All @@ -66,6 +84,23 @@ export function useFirestoreDocData<T = unknown>(
);
}

/**
* Suscribe to Firestore Document changes
*
* @param ref - Reference to the document you want to listen to
* @param options
*/
export function useFirestoreDocDataOnce<T = unknown>(
ref: firestore.DocumentReference,
options?: ReactFireOptions<T>
): T {
return useObservable(
docData(ref, checkIdField(options)).pipe(first()),
'firestore docdataonce: ' + ref.path,
checkStartWithValue(options)
);
}

/**
* Subscribe to a Firestore collection
*
Expand Down
19 changes: 19 additions & 0 deletions sample/src/Firestore.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
SuspenseWithPerf,
useFirestoreCollectionData,
useFirestoreDocData,
useFirestoreDocDataOnce,
useFirestore
} from 'reactfire';

Expand Down Expand Up @@ -32,6 +33,16 @@ const Counter = props => {
);
};

const StaticValue = props => {
const firestore = useFirestore();

const ref = firestore().doc('count/counter');

const { value } = useFirestoreDocDataOnce(ref);

return <span>{value}</span>;
};

const AnimalEntry = ({ saveAnimal }) => {
const [text, setText] = useState('');
const [disabled, setDisabled] = useState(false);
Expand Down Expand Up @@ -121,6 +132,14 @@ const SuspenseWrapper = props => {
>
<Counter />
</SuspenseWithPerf>
<h3>Sample One-time Get</h3>
<SuspenseWithPerf
fallback="connecting to Firestore..."
traceId="firestore-demo-doc"
>
<StaticValue />
</SuspenseWithPerf>

<h3>Sample Collection Listener</h3>
<SuspenseWithPerf
fallback="connecting to Firestore..."
Expand Down