@upgrowth/firekit
v2.0.0
Published
by [Upgrowth](https://www.upgrowth.com.au)
Downloads
5
Readme
Firekit
by Upgrowth
Everything you need to easily make great Firebase-powered React web applications.
$ yarn add @upgrowth/firekit
Concepts
Prop Map
A prop map is an object used when firebinding that specifies instructions for building and providing a series of binding definitions. The keys of the prop map are the keys of the object passed to the child function of a Firebind element, or the props passed to the wrapped component by the firebind HOC. There are several options to define the values, based on its type:
string
The path to get all data from.
{fieldName: 'path/to/data'}
object
A binding definition.
{
fieldName: {
ref: 'path/to/data',
queries: {
startAt: 'firstData',
orderByKey: true
}
}
}
function
Arguments
- An object composed of:
- using data (Firebase component)
- incoming props (firebase HOC)
- loaded data from this prop map
Returns
string
If a string is returned, it is interpreted as the path to the data
{
fieldName: ({id}) => `datas/${id}`
}
Binding Definition Object with Valid Ref
{
fieldName: ({id, count}) => ({
ref: `datas/${id}`,
queries: {
limitToFirst: count,
orderByKey: true
}
})
}
Falsy, or Object Binding Definition with Falsy Ref
If a falsy value is returned, we will wait until it becomes unfalsy.
{
latestPost: {
ref: 'posts',
queries: {
limitToLast: 1,
orderByKey: true
}
},
latestPostRating: ({latestPost}) => latestPost && latestPost.id && `ratings/${latestPost.id}`
}
In this example, it will wait until both of these queries return. You might be better served by using nested Firebind elements, since you will have much more control over the lifecycle and it will be simpler to understand.
Optional Binding Definition Object with Falsy Ref
If the optional
field is set in the binding definition, the context will not wait for the ref to become valid.
{
fieldName: ({userId}) => ({
ref: userId && `profile/${userId}`,
optional: true
})
}
In the above example, we know userId immediately and its absence implies the user is not logged in (and will never be logged in if using SSR). If optional is not set, we would have to wait until our SSR wait times out.
The example in the previous section doesn't handle null state very well; if there are no posts SSR will have to wait until it times out.
This can be resolved using optional
.
{
latestPost: {
ref: 'posts',
queries: {
limitToLast: 1,
orderByKey: true
}
},
latestPostRating: ({latestPost}) => ({
ref: latestPost && latestPost.id && `ratings/${latestPost.id}`,
optional: latestPost === null
})
}
Binding Definition
A binding definition is a recipe for creating a specific Firebase Database Reference, the only successful result of resolving a prop map. It consists of an object with the following:
- ref [string]: the path to the data
- queries [object]: (optional) a series of queries to limit the data returned
- startAt (optional): [number | string | boolean] inclusive start of the query, referring to the requested order
- endAt (optional): [number | string | boolean] inclusive end of the query, referring to the requested order
- limitToFirst (optional): [number] limits query to the first given number of children
- limitToLast (optional): [number] limits query to the last given number of children
- one of the following order by queries (optional):
- orderByChild: [string] sort children by the value of the given path in each child
- orderByKey: [boolean] if true, sort children by key
- orderByValue: [boolean] if true, sort children by their value
- orderByPriority: [boolean] if true, sort children by their priority
- optional [boolean]: (optional) if true, don't wait for this data to be loaded to call the context's onLoad
- permanent [boolean]: (optional) if true, don't ever automatically clean
this subscription up. It will stay in its SubscriptionController and
stay listening to Firebase, until
.cleanUp()
on the controller is manually called. This is useful on subscriptions used by components that are frequently used, but frequently unmounted. - event [string]: (optional) the type of event to listen to (for advanced usages):
"value"
(default)"child_added"
"child_changed"
"child_removed"
"child_moved"
Components
Firebind
Firebind allows you to access a Firebase Realtime Database endpoint, from the application in context, as a component.
Props
- bind [Prop Map]: an object mapping of supplied field names to reference definitions
- whileLoading [React Node]: (optional) what to display while the initial load is happening
- any other props supply data into the binding functions, allowing the functions to be static with respect to outside variables
Children
A function which receives two parameters:
- [object] with the keys supplied to bind with the current data for those references
- [boolean] indicating if the data is still loading (is no whileLoading node is supplied)
Example
const GreetUser = ({userId}) => (
<Firebind bind={{userName: ({userId}) => `users/${userId}/name`}}
userId={userId}
whileLoading={<span className='loading'>Please wait...</span>}>
{({userName}) => <span>Hi {userName}!</span>}
</Firebind>
)
FirebindToApp
If you wish to opt out entirely from Firebase app contexts, even the default one, you can Firebind without one with FirebindToApp. Otherwise identical to Firebind, it also accepts a firebaseApp prop to pass the Firebase app instance.
Example
const coolApp = firebase.initializeApp(OPTIONS, 'coolApp')
const GreetUser = ({userId}) => (
<FirebindToApp firebaseApp={coolApp}
bind={{userName: ({userId}) => `users/${userId}/name`}}
using={{userId}}
whileLoading={<span className='loading'>Please wait...</span>}>
{({userName}) => <span>Hi {userName}!</span>}
</Firebind>
)
FirebaseProvider
FirebaseProvider creates a new Firebase application context that can be consumed by the WithFirebaseApp component and withFirebaseApp HOC. Any nodes under this context will consume the provided app rather than the default.
PLEASE NOTE: You only need to use this if there is more than one Firebase app in your
project, or you are performing SSR or other advanced loading scenarios.
The default context has bindings to the default application in firebase (firebase.app()
).
Props
- onLoad [function]: (optional) a callback with no parameters that will be called when all provided non-optional bindings have loaded
- either of:
- firebaseApp [Firebase App]: the Firebase App to create the context for
- config [object]: the Firebase client configuration to create the firebase app to create the context for
Example
import firebase from 'firebase'
const specialApp = firebase.initializeApp(FIREBASE_CONFIG, 'special')
const App = () => {
<div>
<Firebind bind={{data: 'site'}}>
{({data}) =>
<div>
The data on the default app at 'site' is {data}
</div>
}
</Firebind>
<FirebaseProvider config={FIREBASE_CONFIG}
onLoad={() => window.alert('Enjoy your data!')}>
<Firebind bind={{data: 'site'}}>
{({data}) =>
<div>
The data on the special app at 'site' is {data}
</div>
}
</Firebind>
</FirebaseProvider>
</div>
}
WithFirebaseApp
WithFirebaseApp allows you to access the Firebase application context created by a FirebaseProvider.
PLEASE NOTE: You probably won't need to use this unless you are building components that need to react to the Firebase context automatically.
- If you're only using one firebase app, it's easier and better to use
firebase.app()
- If you're using multiple firebase apps, it's easier and better to use
firebase.app('appName')
- If you want to access the SubscriptionsController, you probably just want to use Firebind.
Children
A function which receives an object, with the following properties:
- app: the Firebase App instance
- database: the Firebase Realtime Database instance
- auth: the Firebase Auth instance
- storage: the Firebase Storage instance (if configured)
- controller: the SubscriptionsController managing subscriptions for the app
<WithFirebaseApp>
{({database}) =>
<Button onClick={() => database.ref('clicks').push('click!')}>
Click!
</Button>
}
</WithFirebaseApp>
HOCs
Many of the elements above have HOC equivalents.
firebind(Component, propMap)
Equivalent to Firebind, useful when the entire component needs access to firebase data, rather than a subset of the render function.
Arguments
- Component: React component to wrap
- propMap [Prop Map] The prop map for the binding
Granted Props
- loading [boolean] indicating loading state
- the result of the requests in the prop map
- all other received props
const GreetUser = firebind(({userName, loading}) => (
loading
? <span className='loading'>Please wait...</span>
: <span>Hi {userName}!</span>
), {
userName: ({userId}) => `users/${userId}/name`
})
firebindToApp(Component, propMap, firebaseApp)
Equivalent to FirebindToApp, useful when the entire component needs access to firebase data, rather than a subset of the render function, and you want to manually specify the Firebase app instance outside of the Firebase app context.
Arguments
- Component: React component to wrap
- propMap [Prop Map] The prop map for the binding
- firebaseApp: The Firebase App Instance to use
Granted Props
- loading [boolean] indicating loading state
- the result of the requests in the prop map
- all other received props
const app = firebase.initializeApp(FIRBASE_OPTS)
const GreetUser = firebindToApp(({userName, loading}) => (
loading
? <span className='loading'>Please wait...</span>
: <span>Hi {userName}!</span>
), {
userName: ({userId}) => `users/${userId}/name`
}, app)
withFirebase(Component)
Equivalent to WithFirebase, useful when the entire component needs access to the Firebase app or SubscriptionController. The same caveats from WithFirebase apply; you probably don't need this and using it may introduce unnecessary coupling and testing complexity, unless your component is already coupled to firebase.
Arguments
- Component React component to wrap
Granted Props
- firebaseApp: an object with
- app: the Firebase App instance
- database: the Firebase Realtime Database instance
- auth: the Firebase Auth instance
- storage: the Firebase Storage instance (if configured)
- controller: the SubscriptionsController managing subscriptions for the app
const GreetUser = withFirebaseApp(({firebaseApp: {database}}) =>
<Button onClick={() => database.ref('clicks').push('click!')}>
Click!
</Button>
)
waitForLoad(Component, Node)
(Deprecated)
A React HoC that renders the given loading indicator until the loading prop is true
.
This reflects the behaviour of firebind
, which passes a loading prop to its children that becomes
true when all specified props have initially loaded
waitForLoad(Component, <MyLoadingIndicator />)
withUserId(Component)
A React HoC that passes the prop userId
with the firebase userid of the current user
Granted Props
- userId [string or null] user ID of the currently signed in user, if signed in, updated automatically
Exposed Classes
SubscriptionsController
SubscriptionsController manages Firebase Realtime Database subscriptions for a specific app context, acting both as a light cache and deduplicator, and provides support for load monitoring for SSR via an interface for creating subscriptions.
Manually constructing SubscriptionControllers is not currently supported. If you wish to instantiate one, create a new application context with FirebaseProvider.
You can retrieve the instance of SubscriptionsController for the context with the WithFirebaseApp component or withFirebaseApp HOC, as the controller field from the parameter or provided prop, respectively.
Instance Methods
.listen(bind, callback)
listen creates a subscription for the provided bind, calling callback when it has been changed.
.cleanUp()
Manually removes orphaned subscriptions, including permanent
ones.
Arguments
- bind, a Binding Definition
- callback, a function receiving one argument, the new value of the binding definition
Returns
An object with the following properties:
- value: the current value, if known, of the subscribed definition
- loading: a boolean indicating the loading state at time of call
- unsubscribe: a function receiving no arguments that will de-register the subscription
Functions
firekit([config])
(Deprecated)
Firekit has historically had a initialisation function, firekit(). This is included in Firekit 2 for backwards compatibility and is no longer required to use any of the systems.
Arguments
- config: (optional except on first call) [object or Firebase App] The Firebase app or configuration object to produce a Firebase app with.
Returns
A object with the following:
- app: the Firebase app instance
- auth: the Firebase auth instance
- database: The Firebase realtime database instance
- TIMESTAMP: a magical constant which will be substituted with the current server-side timestamp upon writing
config(path)
(Deprecated)
Promises to return given configuration. This works on a convention that
in firebase /config/public
is a root node for application configuration
Arguments
- path: [string] path of configuration to access
Returns
A promise that resolves to the data requested