在 React/React Native/Expo 应用中,实现 Vercel 的 useSWR 以查询 Firestore。
在 React/React Native/Expo 应用中,实现 Vercel 的 useSWR 以查询 Firestore。
const { data } = useDocument('users/fernando')
It's that easy.
This library provides the hooks you need for querying Firestore, that you can actually use in production, on every screen.
⚡️ It aims to be the fastest way to use Firestore in a React app, both from a developer experience and app performance perspective.
This library is built on top useSWR, meaning you get all of its awesome benefits out-of-the-box.
You can now fetch, add, and mutate Firestore data with zero boilerplate.
0.14.x!)set, update, and add update your global cache, instantlydocument.data() from Firestore requests.toDate())...along with the features touted by Vercel's incredible SWR library:
"With SWR, components will get a stream of data updates constantly and automatically. Thus, the UI will be always fast and reactive."
If you like this library, give it star and let me know on Twitter!
yarn add @nandorojo/swr-firestore
# or
npm install @nandorojo/swr-firestore
Install firebase:
# if you're using expo:
expo install firebase
# if you aren't using expo:
yarn add firebase
# or
npm i firebase
In the root of your app, create an instance of Fuego and pass it to the FuegoProvider.
If you're using Firebase v8, see this solution for creating your instance of Fuego.
If you're using next.js, this goes in your pages/_app.js file.
App.js
import React from 'react'
import 'firebase/firestore'
import 'firebase/auth'
import { Fuego, FuegoProvider } from '@nandorojo/swr-firestore'
const firebaseConfig = {
// put yours here
}
const fuego = new Fuego(firebaseConfig)
export default function App() {
return (
)
}
Make sure to create your Fuego instance outside of the component. The only argument Fuego takes is your firebase config variable.
Under the hood, this step initializes firebase for you. No need to call firebase.initializeApp.
Assuming you've already completed the setup...
import React from 'react'
import { useDocument } from '@nandorojo/swr-firestore'
import { Text } from 'react-native'
export default function User() {
const user = { id: 'Fernando' }
const { data, update, error } = useDocument(`users/${user.id}`, {
listen: true,
})
if (error) return Error!
if (!data) return Loading...
return Name: {data.name}
}
import React from 'react'
import { useCollection } from '@nandorojo/swr-firestore'
import { Text } from 'react-native'
export default function UserList() {
const { data, update, error } = useCollection(`users`)
if (error) return Error!
if (!data) return Loading...
return data.map(user => {user.name})
}
useDocument accepts a document path as its first argument here. useCollection works similarly.
const { data } = useCollection('users')
const { data } = useDocument(`users/${user.id}`, { listen: true })
const { data } = useCollection('users', {
where: ['name', '==', 'fernando'],
limit: 10,
orderBy: ['age', 'desc'],
listen: true,
})
// pass SWR options
const { data } = useDocument('albums/nothing-was-the-same', {
shouldRetryOnError: false,
onSuccess: console.log,
loadingTimeout: 2000,
})
// pass SWR options
const { data } = useCollection(
'albums',
{
listen: true,
// you can pass multiple where conditions if you want
where: [
['artist', '==', 'Drake'],
['year', '==', '2020'],
],
},
{
shouldRetryOnError: false,
onSuccess: console.log,
loadingTimeout: 2000,
}
)
const { data, add } = useCollection('albums', {
where: ['artist', '==', 'Drake'],
})
const onPress = async () => {
// calling this will automatically update your global cache & Firestore
const documentId = await add({
title: 'Dark Lane Demo Tapes',
artist: 'Drake',
year: '2020',
})
}
const { data, set, update } = useDocument('albums/dark-lane-demo-tapes')
const onReleaseAlbum = () => {
// calling this will automatically update your global cache & Firestore
set(
{
released: true,
},
{ merge: true }
)
// or you could call this:
update({
released: true,
})
}
If you pass null as the collection or document key, the request won't send.
Once the key is set to a string, the request will send.
Get list of users who have you in their friends list
import { useDoormanUser } from 'react-doorman'
const { uid } = useDoormanUser()
const { data } = useCollection(uid ? 'users' : null, {
where: ['friends', 'array-contains', uid],
})
Get your favorite song
const me = { id: 'fernando' }
const { data: user } = useDocument(`users/${me.id}`)
// only send the request once the user.favoriteSong exists!
const { data: song } = useDocument(
user?.favoriteSong ? `songs/${user.favoriteSong}` : null
)
Magically turn any Firestore timestamps into JS date objects! No more .toDate().
Imagine your user document schema looks like this:
type User = {
name: string
lastUpdated: {
date: Date
}
createdAt: Date
}
In order to turn createdAt and lastUpdated.date into JS objects, just use the parseDates field:
In a document query
const { data } = useDocument('user/fernando', {
parseDates: ['createdAt', 'lastUpdated.date'],
})
let createdAt: Date
if (data) {
// ✅ all good! it's a JS Date now.
createdAt = data.createdAt
}
data.createdAt and data.lastUpdated.date are both JS dates now!
In a collection query
const { data } = useCollection('user', {
parseDates: ['createdAt', 'lastUpdated.date'],
})
if (data) {
data.forEach(document => {
document.createdAt // JS date!
})
}
For more explanation on the dates, see issue #4.
If you set ignoreFirestoreDocumentSnapshotField to false, you can access the __snapshot field.
const { data } = useDocument('users/fernando', {
ignoreFirestoreDocumentSnapshotField: false, // default: true
})
if (data) {
const id = data?.__snapshot.id
}
You can do the same for useCollection and useCollectionGroup. The snapshot will be on each item in the data array.
This comes in handy when you are working with forms for data edits:
With Formik
const { data, set } = useDocument('users/fernando', {
ignoreFirestoreDocumentSnapshotField: false,
})
if (!data) return
With state and hooks
const { data, set } = useDocument('users/fernando', {
ignoreFirestoreDocumentSnapshotField: false,
})
const [values, setValues] = useState(null);
useEffect(() => {
if (data) {
setValues(data.__snapshot.data());
}
}, [data]);
Video here.
…
You'll rely on useDocument to query documents.
import React from 'react'
import { useDocument } from '@nandorojo/swr-firestore'
const user = { id: 'Fernando' }
export default () => {
const { data, error } = useDocument(`users/${user.id}`)
}
If you want to set up a listener (or, in Firestore-speak, onSnapshot) just set listen to true.
const { data, error } = useDocument(`users/${user.id}`, { listen: true })
import {
useDocument,
useCollection,
useCollectionGroup, // new!
revalidateDocument,
revalidateCollection,
// these all update BOTH Firestore & the local cache ⚡️
set, // set a firestore document
update, // update a firestore document
fuego, // get the firebase instance used by this lib
getCollection, // prefetch a collection, without being hooked into SWR or React
getDocument, // prefetch a document, without being hooked into SWR or React
} from '@nandorojo/swr-firestore'
useDocument(path, options)const {
data,
set,
update,
deleteDocument,
error,
isValidating,
mutate,
unsubscribe
} = useDocument(path, options)
path required The unique document path for your Firestore document.string | null. If null, the request will not be sent. This is useful if you want to get a user document, but the user ID hasn't loaded yet, for instance.key argument in useSWR. See the SWR docs for more. Functions are not currently supported for this argument.options (optional) A dictionary with added options for the query. Takes the folowing values:listen = false: If true, sets up a listener for this document that updates whenever it changes.useSWR.ignoreFirestoreDocumentSnapshotField = true. See elaboration below.parseDates: An array of string keys that correspond to dates in your document. Example.ignoreFirestoreDocumentSnapshotFieldIf true, docs returned in data will not include the firestore __snapshot field. If false, it will include a __snapshot field. This lets you access the document snapshot, but makes the document not JSON serializable.
By default, it ignores the __snapshot field. This makes it easier for newcomers to use JSON.stringify without weird errors. You must explicitly set it to false to use it.
// include the firestore document snapshots
const { data } = useDocument('users/fernando', {
ignoreFirestoreDocumentSnapshotField: false,
})
if (data) {
const path = data.__snapshot.ref.path
}
The __snapshot field is the exact snapshot returned by Firestore.
See Firestore's snapshot docs for more.
Returns a dictionary with the following values:
set(data, SetOptions?): Extends the firestore document set function.暂无开放 Issues,或尚未同步最近议题。