⚠️ These examples currently rely use ReactFire's concurrent mode features. We'd love PRs that add samples that work with stable builds of React!
- Access your
firebase
app from any component - Access the current user
- Log Page Views with React Router
- Combine Auth, Firestore, and Cloud Storage to Show a User Profile Card
- Manage Loading States
- Lazy Load the Firebase SDKs
- The render-as-you-fetch pattern
- Advanced: Using RxJS observables to combine multiple data sources
Since ReactFire uses React's Context API, any component under a FirebaseAppProvider
can use useFirebaseApp()
to get your initialized app. Plus, all ReactFire hooks will automatically check context to see if a firebase app is available.
// ** INDEX.JS **
const firebaseConfig = {
/* add your config object from the Firebase console */
};
render(
<FirebaseAppProvider firebaseConfig={firebaseConfig}>
<MyApp />
</FirebaseAppProvider>
);
// ** MYCOMPONENT.JS **
function MyComponent(props) {
// useFirestore will get the firebase app from Context!
const documentReference = useFirestore()
.collection('burritos')
.doc('vegetarian');
// ...
}
The useUser()
hook returns the currently signed-in user. Like the other ReactFire Hooks, you need to wrap it in Suspense
or provide a initialData
.
function HomePage(props) {
// no need to use useFirebaseApp - useUser calls it under the hood
const { data: user } = useUser();
return <h1>Welcome Back {user.displayName}!</h1>;
}
Note: useUser
will also automatically lazily import the firebase/auth
SDK if it has not been imported already.
The AuthCheck
component makes it easy to hide/show UI elements based on a user's auth state. It will render its children if a user is signed in, but if they are not signed in, it renders its fallback
prop:
render(
<AuthCheck fallback={<LoginPage />}>
<HomePage />
</AuthCheck>
);
import { useAnalytics } from 'reactfire';
import { Router, Route, Switch } from 'react-router';
function MyPageViewLogger({ location }) {
const analytics = useAnalytics();
// By passing `location.pathname` to the second argument of `useEffect`,
// we only log on first render and when the `pathname` changes
useEffect(() => {
analytics.logEvent('page-view', { path_name: location.pathname });
}, [location.pathname]);
return null;
}
function App() {
const analytics = useAnalytics();
return (
<Router>
<Switch>
<Route exact path="/about" component={<AboutPage />} />
<Route component={<NotFoundPage />} />
</Switch>
<MyPageViewLogger />
</Router>
);
}
import {
AuthCheck,
StorageImage,
useFirestoreDocData,
useUser,
useAuth,
useFirestore
} from 'reactfire';
const DEFAULT_IMAGE_PATH = 'userPhotos/default.jpg';
function ProfileCard() {
// get the current user.
// this is safe because we've wrapped this component in an `AuthCheck` component.
const { data: user } = useUser();
// read the user details from Firestore based on the current user's ID
const userDetailsRef = useFirestore()
.collection('users')
.doc(user.uid);
let { commonName, favoriteAnimal, profileImagePath } = useFirestoreDocData(
userDetailsRef
);
// defend against null field(s)
profileImagePath = profileImagePath || DEFAULT_IMAGE_PATH;
if (!commonName || !favoriteAnimal) {
throw new Error(MissingProfileInfoError);
}
return (
<div>
<h1>{commonName}</h1>
{/*
`StorageImage` converts a Cloud Storage path into a download URL and then renders an image
*/}
<StorageImage style={{ width: '100%' }} storagePath={profileImagePath} />
<span>Your favorite animal is the {favoriteAnimal}</span>
</div>
);
}
function LogInForm() {
const auth = useAuth();
const signIn = () => {
auth.signInWithEmailAndPassword(email, password);
};
return <MySignInForm onSubmit={signIn} />;
}
function ProfilePage() {
return (
{/*
Render a spinner until components are ready
*/}
<Suspense fallback={<MyLoadingSpinner />}>
{/*
Render `ProfileCard` only if a user is signed in.
Otherwise, render `LoginForm`
*/}
<AuthCheck fallback={<LogInForm />}>{ProfileCard}</AuthCheck>
</Suspense>
);
}
ReactFire is designed to integrate with React's Suspense API, but also supports use cases where Suspense isn't needed or wanted.
Say we have a component called Burrito
that uses useFirestoreDoc
:
function Burrito() {
const burritoRef = useFirestore()
.collection('tryreactfire')
.doc('burrito');
// subscribe to the doc. just one line!
// throws a Promise for Suspense to catch,
// and then streams live updates
const burritoDoc = useFirestoreDoc(burritoRef);
const isYummy = burritoDoc.data().yummy;
return <p>The burrito is {isYummy ? 'good' : 'bad'}!</p>;
}
The parent component of Burrito
can use Suspense
to render a fallback
component until useFirestoreDoc
returns a value:
function FoodRatings() {
return (
<Suspense fallback={'loading burrito status...'}>
<Burrito />
</Suspense>
);
}
ReactFire provides an a wrapper around Suspense
called SuspenseWithPerf
that instruments your Suspense
loads with a Firebase Performance Monitoring custom trace. It looks like this:
function FoodRatings() {
return (
<SuspenseWithPerf fallback={'loading burrito status...'} traceId={'load-burrito-status'}>
<Burrito />
</SuspenseWithPerf>
);
}
What if we don't want to use Suspense, or we're server rendering and we know what the initial value should be? In that case we can provide an initial value to any ReactFire hook:
function Burrito() {
const firebaseApp = useFirebaseApp();
const burritoRef = firebaseApp
.firestore()
.collection('tryreactfire')
.doc('burrito');
// subscribe to the doc. just one line!
// returns the `initialData`,
// and then streams live updates
const burritoDoc = useFirestoreDocData(burritoRef, {
initialData: {
yummy: true
}
});
const isYummy = burritoDoc.data().yummy;
return <p>The burrito is {isYummy ? 'good' : 'bad'}!</p>;
}
The parent component of Burrito
now doesn't need to use Suspense
:
function FoodRatings() {
return <Burrito />;
}
This warning can be solved with React's useTransition
hook. Check out the sample code's Firestore example to see how to use this with ReactFire:
Including the Firebase SDKs in your main JS bundle (by using import 'firebase/firestore'
, for example) will increase your bundle size. To get around this, you can lazy load the Firebase SDK with ReactFire. As long as a component has a parent that is a FirebaseAppProvider
, you can use an SDK hook (useFirestore
, useDatabase
, useAuth
, useStorage
) like so:
MyComponent.jsx
import React from 'react';
// WE ARE NOT IMPORTING THE FIRESTORE SDK UP HERE
import { useFirestoreDocData, useFirestore } from 'reactfire';
export function MyComponent(props) {
// automatically lazy loads the Cloud Firestore SDK
const firestore = useFirestore();
const ref = firestore().doc('count/counter');
const data = useFirestoreDocData(ref);
return <h1>{data.value}</h1>;
}
The React docs recommend kicking off reads as early as possible in order to reduce perceived load times. ReactFire offers a number of preload
methods to help you do this.
Call preloadFirestore
(or preloadAuth
, preloadRemoteConfig
, etc) to start fetching a Firebase library in the background. Later, when you call useFirestore
in a component, the useFirestore
hook may not need to suspend if the preload has already completed.
Some Firestore SDKs need to be initialized (firebase.remoteConfig().fetchAndActivate()
), or need to have settings set before any other calls are made (firebase.firestore().enablePersistence()
). This can be done by passing a function returning a promise to the setup
option.
preloadFirestore({
setup: firestore => firestore().enablePersistence()
});
ReactFire's data fetching hooks don't fully support preloading yet. The experimental preloadFirestoreDoc
function allows you to subscribe to a Firestore document if you know you call useFirestoreDoc
somewhere farther down the component tree.
All ReactFire hooks are powered by useObservable
. By calling useObservable
directly, you can subscribe to any observable in the same manner as the built-in ReactFire hooks. If you use RxFire and useObservable
together, you can accomplish more advanced read patterns (like OR queries in Firestore!).