Skip to content

Commit cade209

Browse files
committed
docs(examples): add withConverter example
1 parent d825178 commit cade209

File tree

3 files changed

+187
-58
lines changed

3 files changed

+187
-58
lines changed

examples/react/kitchen-sink/src/App.tsx

Lines changed: 101 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Link, Route, Routes, useLocation } from "react-router-dom";
55
import { CollectionQueryExample } from "./components/CollectionQueryExample";
66
import { IdTokenExample } from "./components/IdTokenExample";
77
import { NestedCollectionsExample } from "./components/NestedCollectionsExample";
8+
import { WithConverterExample } from "./components/WithConverterExample";
89

910
import "./firebase";
1011

@@ -37,6 +38,10 @@ function App() {
3738
path="/nested-collections"
3839
element={<NestedCollectionsExample />}
3940
/>
41+
<Route
42+
path="/typescript-safety"
43+
element={<WithConverterExample />}
44+
/>
4045
</Routes>
4146
</div>
4247
</div>
@@ -49,35 +54,63 @@ function App() {
4954
function Navigation() {
5055
const location = useLocation();
5156

52-
const navItems = [
53-
{ path: "/", label: "Home" },
54-
{ path: "/auth/id-token", label: "ID Token Query" },
55-
{ path: "/firestore/collection-query", label: "Collection Query" },
56-
];
57+
const isActive = (path: string) => location.pathname === path;
5758

5859
return (
5960
<nav className="bg-white shadow-sm border-b">
6061
<div className="max-w-6xl mx-auto px-4">
6162
<div className="flex items-center justify-between h-16">
6263
<div className="flex items-center space-x-8">
63-
<h1 className="text-xl font-bold text-gray-900">
64-
TanStack Query Firebase
65-
</h1>
66-
<div className="flex space-x-4">
67-
{navItems.map((item) => (
68-
<Link
69-
key={item.path}
70-
to={item.path}
71-
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
72-
location.pathname === item.path
73-
? "bg-blue-100 text-blue-700"
74-
: "text-gray-600 hover:text-gray-900 hover:bg-gray-100"
75-
}`}
76-
>
77-
{item.label}
78-
</Link>
79-
))}
80-
</div>
64+
<Link
65+
to="/"
66+
className={`text-lg font-semibold ${
67+
isActive("/") ? "text-blue-600" : "text-gray-900"
68+
}`}
69+
>
70+
TanStack Query Firebase Examples
71+
</Link>
72+
</div>
73+
<div className="flex items-center space-x-6">
74+
<Link
75+
to="/auth/id-token"
76+
className={`text-sm font-medium ${
77+
isActive("/auth/id-token")
78+
? "text-blue-600"
79+
: "text-gray-500 hover:text-gray-700"
80+
}`}
81+
>
82+
Auth: ID Token
83+
</Link>
84+
<Link
85+
to="/firestore/collection-query"
86+
className={`text-sm font-medium ${
87+
isActive("/firestore/collection-query")
88+
? "text-blue-600"
89+
: "text-gray-500 hover:text-gray-700"
90+
}`}
91+
>
92+
Firestore: Collection Query
93+
</Link>
94+
<Link
95+
to="/nested-collections"
96+
className={`text-sm font-medium ${
97+
isActive("/nested-collections")
98+
? "text-blue-600"
99+
: "text-gray-500 hover:text-gray-700"
100+
}`}
101+
>
102+
Nested Collections
103+
</Link>
104+
<Link
105+
to="/typescript-safety"
106+
className={`text-sm font-medium ${
107+
isActive("/typescript-safety")
108+
? "text-blue-600"
109+
: "text-gray-500 hover:text-gray-700"
110+
}`}
111+
>
112+
TypeScript Safety
113+
</Link>
81114
</div>
82115
</div>
83116
</div>
@@ -88,48 +121,58 @@ function Navigation() {
88121
function Home() {
89122
return (
90123
<div className="text-center">
91-
<h1 className="text-4xl font-bold text-gray-900 mb-4">
124+
<h1 className="text-4xl font-bold text-gray-900 mb-6">
92125
TanStack Query Firebase Examples
93126
</h1>
94127
<p className="text-xl text-gray-600 mb-8">
95-
Explore different Firebase hooks and patterns with TanStack Query
128+
Explore different patterns and use cases for Firebase with TanStack
129+
Query
96130
</p>
97-
98-
<div className="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto">
99-
<div className="bg-white rounded-lg shadow-md p-6">
100-
<h2 className="text-2xl font-bold text-gray-900 mb-4">
101-
Authentication
131+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
132+
<Link
133+
to="/auth/id-token"
134+
className="p-6 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow"
135+
>
136+
<h2 className="text-lg font-semibold text-gray-900 mb-2">
137+
Auth: ID Token
102138
</h2>
103-
<p className="text-gray-600 mb-4">
104-
Examples of Firebase Authentication hooks including ID token
105-
management.
139+
<p className="text-gray-600 text-sm">
140+
Get and refresh Firebase ID tokens with proper caching
106141
</p>
107-
<Link
108-
to="/auth/id-token"
109-
className="inline-block bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition-colors"
110-
>
111-
View ID Token Example
112-
</Link>
113-
</div>
114-
115-
<div className="bg-white rounded-lg shadow-md p-6">
116-
<h2 className="text-2xl font-bold text-gray-900 mb-4">Firestore</h2>
117-
<p className="text-gray-600 mb-4">
118-
Examples of Firestore hooks for querying collections and documents.
142+
</Link>
143+
<Link
144+
to="/firestore/collection-query"
145+
className="p-6 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow"
146+
>
147+
<h2 className="text-lg font-semibold text-gray-900 mb-2">
148+
Firestore: Collection Query
149+
</h2>
150+
<p className="text-gray-600 text-sm">
151+
Query Firestore collections with filtering and mutations
119152
</p>
120-
<Link
121-
to="/firestore/collection-query"
122-
className="inline-block bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition-colors"
123-
>
124-
View Collection Query Example
125-
</Link>
126-
</div>
127-
</div>
128-
129-
<div className="mt-12 text-center">
130-
<p className="text-gray-500">
131-
Built with Vite, TanStack Query, React Router, and Firebase
132-
</p>
153+
</Link>
154+
<Link
155+
to="/nested-collections"
156+
className="p-6 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow"
157+
>
158+
<h2 className="text-lg font-semibold text-gray-900 mb-2">
159+
Nested Collections
160+
</h2>
161+
<p className="text-gray-600 text-sm">
162+
Handle nested Firestore collections with real-time updates
163+
</p>
164+
</Link>
165+
<Link
166+
to="/typescript-safety"
167+
className="p-6 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow"
168+
>
169+
<h2 className="text-lg font-semibold text-gray-900 mb-2">
170+
TypeScript Safety
171+
</h2>
172+
<p className="text-gray-600 text-sm">
173+
Resolve DocumentData vs custom interface type issues
174+
</p>
175+
</Link>
133176
</div>
134177
</div>
135178
);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { useCollectionQuery } from "@tanstack-query-firebase/react/firestore";
2+
import {
3+
collection,
4+
type DocumentData,
5+
getFirestore,
6+
type QueryDocumentSnapshot,
7+
query,
8+
type SnapshotOptions,
9+
} from "firebase/firestore";
10+
11+
type Product = {
12+
name: string;
13+
price: number;
14+
};
15+
16+
const productConverter = {
17+
toFirestore(product: Product): DocumentData {
18+
return product;
19+
},
20+
fromFirestore(
21+
snapshot: QueryDocumentSnapshot,
22+
options: SnapshotOptions,
23+
): Product {
24+
const data = snapshot.data(options);
25+
return {
26+
name: data.name,
27+
price: data.price,
28+
};
29+
},
30+
};
31+
32+
export function WithConverterExample() {
33+
const firestore = getFirestore();
34+
const ref = query(
35+
collection(firestore, "products").withConverter(productConverter),
36+
);
37+
38+
const { data, isLoading, isError, error } = useCollectionQuery(ref, {
39+
queryKey: ["products"],
40+
});
41+
42+
if (isLoading) {
43+
return <div>Loading...</div>;
44+
}
45+
46+
if (isError) {
47+
return <div>Error: {error?.message}</div>;
48+
}
49+
50+
const products = data?.docs.map((doc) => doc.data()) ?? [];
51+
52+
return (
53+
<div className="App">
54+
<h1>Products</h1>
55+
<ul>
56+
{products.map((product) => (
57+
<li key={product.name}>
58+
{product.name} - ${product.price}
59+
</li>
60+
))}
61+
</ul>
62+
</div>
63+
);
64+
}

firestore.rules

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,28 @@ service cloud.firestore {
77
allow create: if true;
88
allow get: if true;
99
}
10+
11+
match /products/{document=**} {
12+
allow read: if true;
13+
allow write: if true;
14+
allow create: if true;
15+
allow get: if true;
16+
}
17+
18+
match /conversations/{document=**} {
19+
allow read: if true;
20+
allow write: if true;
21+
allow create: if true;
22+
allow get: if true;
23+
}
24+
25+
match /tasks/{document=**} {
26+
allow read: if true;
27+
allow write: if true;
28+
allow create: if true;
29+
allow get: if true;
30+
}
31+
1032
// match /noread/{document=**} {
1133
// allow read: if false;
1234
// }

0 commit comments

Comments
 (0)