Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/dtinth/fiery

πŸ”₯ Easy declarative modern Firebase binding for React ^_^
https://github.com/dtinth/fiery

firebase typescript umd

Last synced: about 1 month ago
JSON representation

πŸ”₯ Easy declarative modern Firebase binding for React ^_^

Awesome Lists containing this project

README

        

# [fiery](https://dtinth.github.io/fiery/) πŸ”₯

fiery πŸ”₯ is the quickest and easiest way to use **Firebase Authentication** and **Firebase Realtime Database** in a React app. It uses latest React features and patterns such as [render props](https://reactjs.org/docs/render-props.html), [hooks](https://reactjs.org/docs/hooks-intro.html), and [suspense](https://reactjs.org/docs/react-api.html#reactsuspense).

**Jump to:** [Installation](#installation) Β· [Demo](#demo) Β· [API Usage](#api-usage) Β· [Development](#development) Β· [License](#license)

_Not production-ready:_ Although I tested it in my projects and small real-world scenarios, they are only small apps and quick prototypes. Contributions are welcome to make this more suitable for production usage.

## Installation

You can install fiery πŸ”₯ from npm:

```
npm install --save fiery
```

If you’re using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html#create-react-app) or a module bundler such as webpack or parcel, you can import fiery using the `import` statement:

```
import fiery from 'fiery'
```

If you’re [using React with no build tooling](https://reactjs.org/docs/add-react-to-a-website.html), a UMD version is also available (make sure to include `react`, `react-dom`, and `firebase` before including `fiery`):

```

```

**Important:** Make sure you are using a version of React that supports the [Hooks API](https://reactjs.org/docs/hooks-intro.html).

## Demo

[Try it out!](https://dtinth.github.io/fiery/#DistributedCounter)

```js
// Demo: DistributedCounter
// This demo app uses only Functional Components!

// Normal Firebase stuff...
//
const counterRef = firebase.database().ref('demos/counter')
const counterDecrement = () => counterRef.transaction(c => c - 1)
const counterIncrement = () => counterRef.transaction(c => c + 1)

function DistributedCounter() {
// The `useFirebaseDatabase` hook makes this component automatically
// subscribe to Firebase Realtime Database. When the data change,
// this component is automatically re-rendered.
//
// This is possible thanks to the Hooks API, introduced in React 16.7.0-alpha.0.
//
const counterState = fiery.useFirebaseDatabase(counterRef)
return (



-


{counterState.loading ? (

) : counterState.failed ? (

) : (
{counterState.data}
)}


+


)
}

ReactDOM.render(
,
document.getElementById('DistributedCounter')
)
```

```js
// Demo: GuestbookApp
// This demo app uses only Functional Components!
function GuestbookApp() {
return (





)
}

/**
* The navigation bar
*/
function Nav() {
return (


{/* Subscribe to the authentication state.
NOTE: We use the Render Props technique here to localize updates
to a single component without requiring a new
React component. */}

{/* `authState` contains `loading`, `failed`, and `data` properties. */}
{authState =>
authState.loading ? (

) : authState.failed ? (

) : authState.data ? (

) : (

)
}


)
}

// The `signIn` and `signOut` functions uses the normal Firebase auth functions.
// No new APIs to learn here!
//
function signIn() {
firebase
.auth()
.signInWithPopup(new firebase.auth.GithubAuthProvider())
.catch(e => window.alert(`Sorry, cannot sign in! ${e}`))
}
function signOut() {
if (window.confirm('RLY SIGN OUT?')) firebase.auth().signOut()
}

/**
* The list of guestbook entries.
*/
function GuestbookList() {
// The `useFirebaseDatabase` hook makes this component automatically
// subscribe to Firebase Realtime Database. When the data change,
// this component is automatically re-rendered.
//
const guestbookState = fiery.useFirebaseDatabase(
firebase
.database()
.ref('demos/guestbook')
.orderByChild('time')
.limitToLast(8)
)
return (

{guestbookState.loading ? (

) : guestbookState.failed ? (

) : (
Object.keys(guestbookState.data).map(key => (

))
)}

)
}

/**
* The form to submit a guestbook entry.
*/
function GuestbookForm() {
// The `useFirebaseAuth` hook makes this component automatically
// subscribe to Firebase Authentication state. When user signs in
// or signs out, this component will automatically update.
//
const userState = fiery.useFirebaseAuth()
return userState.loading ? (

) : userState.failed ? (

) : userState.data ? (
submitForm(text, userState.data)} />
) : (

)
}

// Write to Firebase Realtime Database using the familiar Firebase SDK!
//
function submitForm(text, user) {
return firebase
.database()
.ref('demos/guestbook')
.push({
time: firebase.database.ServerValue.TIMESTAMP,
name: user.displayName,
text: text
})
}

// Render the app...
//
ReactDOM.render(, document.getElementById('GuestbookApp'))
```

```js
// Demo: SuspenseDemo
// In this demo, there are no checks for Loading/Error state.
// Loading state is handled by React.Suspense.
// Error state is handled by using an Error Boundary.
// WARNING: Unstable API!

function SuspenseDemo() {
return (
// Set up an Error Boundary to catch errors.



)
}

function SectionSelector() {
// `error` β€” always results in an error.
// `protected` β€” only logged in users can see. If you log out, you will get en error.
// `even` β€” only accessible when the Counter (1st demo) contains an even number.
const sections = ['intro', 'bridge', 'chorus', 'error', 'protected', 'even']
const [currentSection, setCurrentSection] = React.useState('intro')
return (

setCurrentSection(tab)}
/>

{/* Use `React.Suspense` to display a loading UI
if any child component is not ready to render */}
}>




)
}

function SectionContent({ sectionName }) {
const dataRef = firebase.database().ref(`demos/tabs/${sectionName}`)
const dataState = fiery.useFirebaseDatabase(dataRef)

return (


{/* Use `.unstable_read()` to read the data out of Firebase.
Suspends rendering if data is not ready. */}
{sectionName}: {dataState.unstable_read()}

)
}

ReactDOM.render(, document.getElementById('SuspenseDemo'))
```

## API Usage

fiery πŸ”₯ provides both [hooks](https://reactjs.org/hooks)-based and [render props](https://reactjs.org/docs/render-props.html)-based APIs ([rationale](https://twitter.com/dtinth/status/1055874999377047553)). These APIs will inject **DataState objects** into your component.

A **DataState object** will contain `loading`, `failed`, `data`, `error`, and `retry` properties. It also contains an _experimental_ method `unstable_read()` to make React [suspend](https://reactjs.org/docs/react-api.html#reactsuspense) rendering until the data is loaded.

### Synopsis

- Using Hooks

```js
function HookSynopsis() {
const counterState = fiery.useFirebaseDatabase(counterRef)

if (counterState.loading) {
return
} else if (counterState.failed) {
return (

)
} else {
return counterState.data
}
}
```

- Suspense + Hooks

```js
function SuspenseHookSynopsis() {
const counterState = fiery.useFirebaseDatabase(counterRef)

return counterState.unstable_read()
}
```

- Using Render Props

```js
function RenderPropsSynopsis() {
return (

{counterState =>
counterState.loading ? (

) : counterState.failed ? (

) : (
counterState.data
)
}

)
}
```

- Suspense + Render Props
```js
function SuspenseRenderPropsSynopsis() {
return (

{counterState => counterState.unstable_read()}

)
}
```

### `fiery.useFirebaseAuth()`

Subscribe and use authentication state.

- Returns a `fiery.DataState` wrapping a [`firebase.User`](https://firebase.google.com/docs/reference/js/firebase.User) object (if signed in) or `null` (if signed out).

### `fiery.Auth`

Render prop version of `fiery.useFirebaseAuth`.

Takes a single prop:

- `children` β€” A **function** that determines how the authentication state should be rendered.
It will be called with a `RemoteDataState` wrapping a [`firebase.User`](https://firebase.google.com/docs/reference/js/firebase.User) object (if signed in) or `null` (if signed out).

### `fiery.useFirebaseDatabase(dataRef: firebase.database.Query)`

Subscribe and use data from Firebase Realtime Database.

- `dataRef` β€” A [`firebase.database.Reference`](https://firebase.google.com/docs/reference/js/firebase.database.Reference) representing the data reference to fetch.
- Returns a `fiery.DataState` wrapping the data (if it exists) or `null` otherwise.

### `fiery.Data`

Render prop version of `fiery.useFirebaseDatabase`.

Takes two props:

- `dataRef` β€” A [`firebase.database.Reference`](https://firebase.google.com/docs/reference/js/firebase.database.Reference) representing the data reference to fetch.
- `children` β€” A **function** that determines how the data state should be rendered.
It will be called with a `fiery.DataState` wrapping the data (if it exists) or `null` otherwise.

### `fiery.DataState` β€” Representing remote data.

When displaying data from remote sources, the data may not be immediately available β€” it may still be loading, or may have failed to load.

fiery πŸ”₯ represents this by providing you a `DataState` object, which is a plain JS object with these properties:

- `loading` β€” A **boolean** representing whether data is being actively loaded or not.
- `failed` β€” A **boolean** representing whether data failed to load or not. **Note:** When retrying, the `failed` flag will stay `true` until new data has been loaded successfully.
- `data` β€” The data of type **T**. May be `undefined` if `loading || failed`.
- `error` β€” The **Error**. May be `undefined` if `!failed`.
- `retry` β€” A **function** that may be called to retry the operation. May be `undefined` if `!failed || loading`.

If you use TypeScript, our typings file can help preventing you from accessing the `data` in loading or failed state. Refer to this table.

| `loading` | `failed` | `data` | `error` | `retry` | Remarks |
| --------- | -------- | --------------- | ----------- | ------------ | ------------ |
| `true` | `false` | `T | undefined` | `undefined` | `undefined` | Initial load |
| `true` | `true` | `T | undefined` | `Error` | `undefined` | Retrying |
| `false` | `false` | `T` | `undefined` | `undefined` | Completed |
| `false` | `true` | `T | undefined` | `Error` | `() => void` | Error |

**Suspense support:** A DataState object also contains an _experimental_ method, `unstable_read()` for reading the data while rendering. It [suspends rendering](https://reactjs.org/docs/react-api.html#reactsuspense) if data from Firebase is not ready. Note that this uses an _unstable_ API and is subject to change.

### Looking for Firebase Firestore bindings?

Please contribute!

## Development

This project uses Yarn.

### Dependencies

To install dependencies, run:

```
yarn
```

### Development

Run:

```
yarn dev
```

This will run Rollup in watch mode and generate `umd/fiery.js`.
It will also re-generate the documentation site on change.

### Building

To build the library once, run:

```
yarn build
```

This will generate `umd/fiery.js`.

### Docs

The documentation website is generated from `README.md`.

To generate the docs, run:

```
yarn docs
```

## License

MIT.