Skip to content

Commit e15c0fb

Browse files
committed
refactor: organize per-product
now that we understand the api better, I'm breaking the components and hooks into per-product folders so that they're easier to read/test/maintain
1 parent d5a1543 commit e15c0fb

File tree

18 files changed

+342
-316
lines changed

18 files changed

+342
-316
lines changed

reactfire/auth/auth.test.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { cleanup, render } from '@testing-library/react';
2+
import { auth } from 'firebase/app';
3+
import 'jest-dom/extend-expect';
4+
import * as React from 'react';
5+
import { AuthCheck } from '.';
6+
import { FirebaseAppProvider } from '..';
7+
8+
const mockAuth = jest.fn(() => {
9+
return {
10+
onIdTokenChanged: jest.fn()
11+
};
12+
});
13+
14+
const mockFirebase = {
15+
auth: mockAuth
16+
};
17+
18+
const Provider = ({ children }) => (
19+
<FirebaseAppProvider firebaseApp={mockFirebase}>
20+
{children}
21+
</FirebaseAppProvider>
22+
);
23+
24+
describe('AuthCheck', () => {
25+
afterEach(() => {
26+
cleanup();
27+
jest.clearAllMocks();
28+
});
29+
30+
it('can find firebase Auth from Context', () => {
31+
expect(() =>
32+
render(
33+
<Provider>
34+
<React.Suspense fallback={'loading'}>
35+
<AuthCheck fallback={'loading'}>{'children'}</AuthCheck>
36+
</React.Suspense>
37+
</Provider>
38+
)
39+
).not.toThrow();
40+
});
41+
42+
it('can use firebase Auth from props', () => {
43+
expect(() =>
44+
render(
45+
<React.Suspense fallback={'loading'}>
46+
<AuthCheck
47+
fallback={<h1>not signed in</h1>}
48+
auth={(mockFirebase.auth() as unknown) as auth.Auth}
49+
>
50+
{'signed in'}
51+
</AuthCheck>
52+
</React.Suspense>
53+
)
54+
).not.toThrow();
55+
});
56+
57+
test.todo('renders the fallback if a user is not signed in');
58+
59+
test.todo('renders children if a user is logged in');
60+
61+
test.todo('checks requiredClaims');
62+
});
63+
64+
describe('useUser', () => {
65+
test.todo('can find firebase.auth() from Context');
66+
67+
test.todo('throws an error if firebase.auth() is not available');
68+
69+
test.todo('returns the same value as firebase.auth().currentUser()');
70+
});

reactfire/components.tsx renamed to reactfire/auth/index.tsx

Lines changed: 26 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1+
import { auth, User } from 'firebase/app';
12
import * as React from 'react';
2-
import { auth, performance, User } from 'firebase/app';
3-
import { useUser, useFirebaseApp } from './index';
4-
const { Suspense, useState, useLayoutEffect } = React;
3+
import { user } from 'rxfire/auth';
4+
import { useObservable, useFirebaseApp, ReactFireOptions } from '..';
55

6-
export interface SuspensePerfProps {
7-
children: React.ReactNode;
8-
traceId: string;
9-
fallback: React.ReactNode;
10-
firePerf?: performance.Performance; // TODO(jeff): Add firePerf here when it's available
11-
}
12-
13-
function getPerfFromContext(): performance.Performance {
6+
function getAuthFromContext(): auth.Auth {
147
const firebaseApp = useFirebaseApp();
158

169
if (!firebaseApp) {
@@ -19,39 +12,34 @@ function getPerfFromContext(): performance.Performance {
1912
);
2013
}
2114

22-
const perfFunc = firebaseApp.performance;
15+
const authFunc = firebaseApp.auth;
2316

24-
if (!perfFunc || !perfFunc()) {
17+
if (!authFunc || !authFunc()) {
2518
throw new Error(
26-
"No perf object off of Firebase. Did you forget to import 'firebase/performance' in a component?"
19+
"No auth object off of Firebase. Did you forget to import 'firebase/auth' in a component?"
2720
);
2821
}
2922

30-
return perfFunc();
23+
return authFunc();
3124
}
3225

33-
export function SuspenseWithPerf({
34-
children,
35-
traceId,
36-
fallback,
37-
firePerf
38-
}: SuspensePerfProps) {
39-
firePerf = firePerf || getPerfFromContext();
40-
const trace = React.useMemo(() => firePerf.trace(traceId), [traceId]);
41-
42-
const Fallback = () => {
43-
useLayoutEffect(() => {
44-
trace.start();
45-
46-
return () => {
47-
trace.stop();
48-
};
49-
}, []);
50-
51-
return <>{fallback}</>;
52-
};
53-
54-
return <Suspense fallback={<Fallback />}>{children}</Suspense>;
26+
/**
27+
* Subscribe to Firebase auth state changes, including token refresh
28+
*
29+
* @param auth - the [firebase.auth](https://firebase.google.com/docs/reference/js/firebase.auth) object
30+
* @param options
31+
*/
32+
export function useUser<T = unknown>(
33+
auth?: auth.Auth,
34+
options?: ReactFireOptions<T>
35+
): User | T {
36+
auth = auth || getAuthFromContext();
37+
38+
return useObservable(
39+
user(auth),
40+
'user',
41+
options ? options.startWithValue : undefined
42+
);
5543
}
5644

5745
export interface AuthCheckProps {
@@ -69,7 +57,7 @@ export function AuthCheck({
6957
}: AuthCheckProps): React.ReactNode {
7058
const user = useUser<User>(auth);
7159

72-
useLayoutEffect(() => {
60+
React.useLayoutEffect(() => {
7361
// TODO(jeff) see if this actually works
7462
if (requiredClaims) {
7563
throw user.getIdTokenResult().then(idTokenResult => {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import 'jest-dom/extend-expect';
2+
3+
describe('Realtime Database (RTDB)', () => {
4+
describe('useDatabaseObject', () => {
5+
test.todo("returns the same value as ref.on('value')");
6+
});
7+
8+
describe('useDatabaseList', () => {
9+
test.todo("returns the same value as ref.on('value')");
10+
});
11+
});

reactfire/database/index.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { database } from 'firebase/app';
2+
import { list, object, QueryChange } from 'rxfire/database';
3+
import { ReactFireOptions, useObservable } from '..';
4+
5+
/**
6+
* Subscribe to a Realtime Database object
7+
*
8+
* @param ref - Reference to the DB object you want to listen to
9+
* @param options
10+
*/
11+
export function useDatabaseObject<T = unknown>(
12+
ref: database.Reference,
13+
options?: ReactFireOptions<T>
14+
): QueryChange | T {
15+
return useObservable(
16+
object(ref),
17+
ref.toString(),
18+
options ? options.startWithValue : undefined
19+
);
20+
}
21+
22+
/**
23+
* Subscribe to a Realtime Database list
24+
*
25+
* @param ref - Reference to the DB List you want to listen to
26+
* @param options
27+
*/
28+
export function useDatabaseList<T = { [key: string]: unknown }>(
29+
ref: database.Reference | database.Query,
30+
options?: ReactFireOptions<T[]>
31+
): QueryChange[] | T[] {
32+
return useObservable(
33+
list(ref),
34+
ref.toString(),
35+
options ? options.startWithValue : undefined
36+
);
37+
}

reactfire/firebaseContext.test.tsx renamed to reactfire/firebaseApp/firebaseApp.test.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { renderHook, act } from '@testing-library/react-hooks';
2-
import { render, cleanup } from '@testing-library/react';
3-
import * as React from 'react';
1+
import { cleanup, render } from '@testing-library/react';
2+
import { renderHook } from '@testing-library/react-hooks';
3+
import * as firebase from 'firebase/app';
44
import 'jest-dom/extend-expect';
5+
import * as React from 'react';
6+
import { useFirebaseApp } from '.';
57
import { FirebaseAppProvider } from './index';
6-
import * as firebase from 'firebase/app';
7-
import { useFirebaseApp } from './firebaseContext';
88

99
afterEach(cleanup);
1010

File renamed without changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { renderHook, act } from '@testing-library/react-hooks';
2+
import * as React from 'react';
3+
import 'jest-dom/extend-expect';
4+
5+
describe('Firestore', () => {
6+
describe('useFirestoreDoc', () => {
7+
test.todo('returns the same value as ref.onSnapshot()');
8+
});
9+
10+
describe('useFirestoreCollection', () => {
11+
test.todo('returns the same value as ref.onSnapshot()');
12+
});
13+
});

reactfire/firestore/index.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { firestore } from 'firebase/app';
2+
import { doc, fromCollectionRef } from 'rxfire/firestore';
3+
import { ReactFireOptions, useObservable } from '..';
4+
5+
/**
6+
* Suscribe to Firestore Document changes
7+
*
8+
* @param ref - Reference to the document you want to listen to
9+
* @param options
10+
*/
11+
export function useFirestoreDoc<T = unknown>(
12+
ref: firestore.DocumentReference,
13+
options?: ReactFireOptions<T>
14+
): firestore.DocumentSnapshot | T {
15+
return useObservable(
16+
doc(ref),
17+
ref.path,
18+
options ? options.startWithValue : undefined
19+
);
20+
}
21+
22+
/**
23+
* Subscribe to a Firestore collection
24+
*
25+
* @param ref - Reference to the collection you want to listen to
26+
* @param options
27+
*/
28+
export function useFirestoreCollection<T = { [key: string]: unknown }>(
29+
ref: firestore.CollectionReference,
30+
options?: ReactFireOptions<T[]>
31+
): firestore.QuerySnapshot | T[] {
32+
return useObservable(
33+
fromCollectionRef(ref),
34+
ref.path,
35+
options ? options.startWithValue : undefined
36+
);
37+
}

reactfire/index.test.ts

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)