Realtime Database
The @react-query-firebase/database
package provides hooks fetching and mutating data on Realtime Database.
Usage
To use the hooks, export your Database
instance, e.g:
import { initializeApp } from 'firebase/app';
import { getDatabase } from 'firebase/database';
const firebase = initializeApp({
...
});
export const database = getDatabase(firebase);
useDatabaseSnapshot
The useDatabaseSnapshot
hook can be used to query a database reference and return a DataSnapshot
,
whilst wrapping around the useQuery
hook.
import React from "react";
import { ref } from "firebase/database";
import { useDatabaseSnapshot } from "@react-query-firebase/database";
import { database } from "../firebase";
function Profile({ id }: { id: string }) {
const dbRef = ref(database, `users/${id}`);
const query = useDatabaseSnapshot(["user", id], dbRef);
if (query.isLoading) {
return <div>Loading...</div>;
}
const snapshot = query.data;
if (!snapshot.exists()) {
return <div>Please sign-in!</div>;
}
return <div>Welcome {snapshot.val().displayName}</div>;
}
To subscribe to value changes on the reference, pass the subscribe
option to the hook:
// Subscribe to value changes
const query = useDatabaseSnapshot(["user", id], dbRef, {
subscribe: true,
});
You can also pass along useQuery
hook options, for example
to return a specific value from the snapshot and be notified on success:
const query = useDatabaseSnapshot[("user", id], dbRef, {
select(snapshot) {
if (snapshot.exists()) {
return snapshot.val().displayName;
}
return null;
},
onSuccess(displayName) {
console.log("Fetched the users display name!", displayName);
},
});
if (query.isSuccess) {
console.log("Users display name is: ", query.data);
}
TypeScript users can also provide a return type for the data response:
const query = useDatabaseSnapshot<number | null>(["user", id], dbRef, {
select(snapshot) {
if (snapshot.exists()) {
return snapshot.val().age;
}
return null;
},
});
if (query.isSuccess) {
// query data will be typed as number | null
console.log("Users age is: ", query.data);
}
useDatabaseValue
The useDatabaseValue
hook can be used to query a database reference and return the contents of the ref.
The hook wraps around the useQuery
hook.
import React from "react";
import { ref } from "firebase/database";
import { useDatabaseValue } from "@react-query-firebase/database";
import { database } from "../firebase";
function Profile({ id }: { id: string }) {
const dbRef = ref(database, `users/${id}`);
const query = useDatabaseValue(["user", id], dbRef);
if (query.isLoading) {
return <div>Loading...</div>;
}
if (!query.data) {
return <div>Please sign-in!</div>;
}
return <div>Welcome {query.data.displayName}</div>;
}
To subscribe to value changes on the reference, pass the subscribe
option to the hook:
// Subscribe to value changes
const query = useDatabaseValue(["user", id], dbRef, {
subscribe: true,
});
You can also pass along useQuery
hook options, for example
to return a specific value from the snapshot and be notified on success:
const query = useDatabaseValue(["user", id], dbRef, undefined, {
select(data) {
return data?.displayName;
},
onSuccess(displayName) {
console.log("Fetched the users display name!", displayName);
},
});
if (query.isSuccess) {
console.log("Users display name is: ", query.data);
}
TypeScript users can also provide a return type for the data response:
type Profile = {
displayName: string;
age: number;
};
const query = useDatabaseValue<Profile, number | null>(
["user", id],
dbRef,
undefined,
{
select(data) {
return data?.age;
},
}
);
if (query.isSuccess) {
// query data will be typed as number | null
console.log("Users age is: ", query.data);
}
Working with arrays
By default the useDatabaseValue
hook will return the entire contents of the ref as it appears within
the database (an object or value). If the database reference provided is an object which is designed to
act as a list of ordered data, the hook
can also return an array of ordered data instead.
For example, assume you push new events to an audit trail of the user:
const auditRef = ref(db, "users/123/audit");
await set(push(auditRef), "User logged in!");
// Sometime later
await set(push(auditRef), "User logged out!");
We can now return the data as an array with ordered values by providing the toArray
option:
const auditRef = ref(db, "users/123/audit");
const query = useDatabaseValue(["user", "123", "audit"], dbRef, {
toArray: true,
});
if (query.isSuccess) {
console.log(query.data);
/**
* [
* "User logged in!",
* "User logged out!",
* ]
*/
}
useDatabaseSetMutation
To set data in the database, use the useDatabaseSetMutation
hook.
The hook wraps around the useMutation
hook.
const dbRef = ref(database, `user/123`);
const mutation = useDatabaseSetMutation(dbRef);
mutation.mutate({
displayName: "John Doe",
});
To set data with priority, provide the priority
option:
const mutation = useDatabaseSetMutation(dbRef, {
priority: "high",
});
TypeScript users can override the generic unknown
type on the setting of data:
const dbRef = ref(database, `user/123`);
type User = {
displayName: string;
age: number;
};
const mutation = useDatabaseSetMutation<User>(dbRef);
// Mutate call is now typed as `User`
mutation.mutate({
displayName: "John Doe",
age: 18,
});
useDatabaseUpdateMutation
To update data in the database, use the useDatabaseUpdateMutation
hook.
The hook wraps around the useMutation
hook.
const dbRef = ref(database, `user/123`);
// Example data set in the database
await set(dbRef, {
displayName: "John Doe",
address: {
line1: "Sunset Boulevard",
},
});
const mutation = useDatabaseUpdateMutation(dbRef);
mutation.mutate({
displayName: "John Doe",
"address/line1": "Sunrise Boulevard",
});
TypeScript users can override the generic Record<string, unknown>
type on the updating of data:
const dbRef = ref(database, `user/123`);
type User = {
displayName: string;
"address/line1": string;
};
const mutation = useDatabaseUpdateMutation<User>(dbRef);
// Mutate call is now typed as `User`
mutation.mutate({
displayName: "John Doe",
"address/line1": "Sunrise Boulevard",
});
useDatabaseRemoveMutation
To remove data in the database, use the useDatabaseRemoveMutation
hook.
The hook wraps around the useMutation
hook.
const dbRef = ref(db, "user/567");
const mutation = useDatabaseRemoveMutation(dbRef);
// Calling mutate will delete the user.
mutation.mutate();
useDatabaseTransactionMutation
To perform a database transaction, use the useDatabaseTransactionMutation
hook.
The hook wraps around the useMutation
hook.
const dbRef = ref(db, "post/123");
const mutation = useDatabaseTransactionMutation(dbRef, (post) => {
if (post) {
post.likes++;
}
return post;
});
// Calling mutate trigger the transaction
mutation.mutate();
If you wish to get the result of the transaction (a TransactionResult
),
either await the mutation or wait for it to be successful:
const dbRef = ref(db, "post/123");
const mutation = useDatabaseTransactionMutation(dbRef, (post) => {
if (post) {
post.likes++;
}
return post;
});
// Option 1: Await the mutation
const transactionResult = await mutation.mutateAsync();
// Option 2: Wait for success
if (mutation.isSuccess) {
const transactionResult = mutation.data;
console.log(transactionResult.committed);
console.log(transactionResult.snapshot);
}
TypeScript users can provide a typed transaction value rather than the default any
type:
const dbRef = ref(db, "post/123");
type Post = {
title: string;
likes: number;
}
const mutation = useDatabaseTransactionMutation<Post>(dbRef, (post: Post | null) => {
if (post) {
post.likes++;
}
return post;
});
// Calling mutate trigger the transaction
mutation.mutate();