├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .npmignore
├── .prettierrc
├── .travis.yml
├── LICENSE
├── README.md
├── __tests__
├── .eslintrc
├── firebaseApp.test.js
├── index.test.js
└── useFirebase.test.js
├── package-lock.json
├── package.json
└── src
├── firebaseApp.js
├── index.js
└── useFirebase.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | end_of_line = lf
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 | indent_size = 2
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "amex",
3 | "rules": {
4 | "consistent-return": 0,
5 | "react/jsx-filename-extension": 0,
6 | "symbol-description": 0
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 | .DS_Store
14 |
15 | # Directory for instrumented libs generated by jscoverage/JSCover
16 | lib-cov
17 |
18 | # Coverage directory used by tools like istanbul
19 | coverage
20 |
21 | # nyc test coverage
22 | .nyc_output
23 |
24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
25 | .grunt
26 |
27 | # Bower dependency directory (https://bower.io/)
28 | bower_components
29 |
30 | # node-waf configuration
31 | .lock-wscript
32 |
33 | # Compiled binary addons (https://nodejs.org/api/addons.html)
34 | build/Release
35 |
36 | # Dependency directories
37 | node_modules/
38 | jspm_packages/
39 |
40 | # TypeScript v1 declaration files
41 | typings/
42 |
43 | # Optional npm cache directory
44 | .npm
45 |
46 | # Optional eslint cache
47 | .eslintcache
48 |
49 | # Optional REPL history
50 | .node_repl_history
51 |
52 | # Output of 'npm pack'
53 | *.tgz
54 |
55 | # Yarn Integrity file
56 | .yarn-integrity
57 |
58 | # dotenv environment variables file
59 | .env
60 |
61 | # next.js build output
62 | .next
63 |
64 | # lib
65 | lib/
66 | dist/
67 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | src
3 | .editorconfig
4 | .eslintrc
5 | .babelrc
6 | __tests__
7 | coverage
8 | .prettierrc
9 | .travis.yml
10 | .all-contributorsrc
11 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "es5"
4 | }
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "10"
5 |
6 | jobs:
7 | include:
8 | - stage: test
9 | script: npm t
10 | - stage: npm release
11 | if: branch = master
12 | deploy:
13 | provider: npm
14 | email: $NPM_EMAIL
15 | api_key: $NPM_TOKEN
16 | skip_cleanup: true
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-present Donavon West
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @use-firebase/app
2 |
3 | A custom React Hook that impliments Firebase's App object.
4 |
5 | [](https://badge.fury.io/js/%40use-firebase%2Fapp)
6 | [](#contributors)
7 |
8 | ## Installation
9 |
10 | ```bash
11 | $ npm i @use-firebase/app
12 | ```
13 |
14 | or
15 |
16 | ```bash
17 | $ yarn add @use-firebase/app
18 | ```
19 |
20 | ## API
21 |
22 | You must first import the package like so.
23 |
24 | ```js
25 | import { FirebaseAppProvider, useFirebaseApp } from '@use-firebase/app';
26 | ```
27 |
28 | ### FirebaseAppProvider
29 |
30 | Return a Firebase application provider. You must wrap your app inside
31 | of an ``.
32 |
33 | ```jsx
34 |
35 |
36 |
37 | ```
38 |
39 | #### Props
40 |
41 | Here are the parameters that you can use.
42 |
43 | | Parameter | Description |
44 | | :-------- | :-------------------------------------------------------------------------------------------------- |
45 | | `config` | A configuration object. See below for instructions on how to obtain this from the Firebase console. |
46 | | `name` | An optional name for your app. Default = `[DEFAULT]`. |
47 |
48 | ##### Obtaining `config` information from the Firebase console
49 |
50 | 1. Go to your project page on the [Firebase console](https://console.firebase.google.com).
51 | 1. Next to `Project Overview` you should see a gear icon.
52 | 1. Click it and select `Project settings` from the menu.
53 | 1. On the `General` tab, you should see a `Your apps` section.
54 | 1. Click on the web icon (i.e. `>`)
55 | 1. Copy just the `var config` information and not any of the HTML tags. It should look somthing like this.
56 |
57 | ```js
58 | var config = {
59 | apiKey: 'AIzaSy7IGdsxh_8XNxKn6H4d9yKBH5qnD_B2xbs',
60 | authDomain: 'my-app-87543.firebaseapp.com',
61 | databaseURL: 'https://my-app-87543.firebaseio.com',
62 | projectId: 'my-app-87543',
63 | storageBucket: 'my-app-87543.appspot.com',
64 | messagingSenderId: '423450585517',
65 | };
66 | ```
67 |
68 | ### useFirebaseApp
69 |
70 | ```js
71 | const app = useFirebaseApp();
72 | ```
73 |
74 | `useFirebaseApp` is a custom hook that you can use in your components to
75 | obtain the `app` object created by `FirebaseAppProvider`.
76 |
77 | ## Live demo
78 |
79 | TODO
80 |
81 | ## License
82 |
83 | **[MIT](LICENSE)** Licensed
84 |
--------------------------------------------------------------------------------
/__tests__/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "amex/test",
3 | "rules": {
4 | "react/prop-types": 0,
5 | "react/jsx-filename-extension": 0
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/__tests__/firebaseApp.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import 'jest-dom/extend-expect';
3 | import { render } from 'react-testing-library';
4 |
5 | import { FirebaseAppProvider, useFirebaseApp } from '../src/firebaseApp';
6 |
7 | describe('FirebaseAppProvider', () => {
8 | test('throws and exception if not as descendant on ', () => {
9 | // expect(index.FirebaseAppProvider).toBe(FirebaseAppProvider);
10 | // expect(index.useFirebaseApp).toBe(useFirebaseApp);
11 | });
12 | });
13 |
14 | describe('useFirebaseApp', () => {
15 | const mockFirebase = {
16 | initializeApp: (config, name) => ({
17 | options: config,
18 | name,
19 | }),
20 | apps: [
21 | { name: 'foo' },
22 | ],
23 | };
24 |
25 | test('throws and exception if not as descendant of ', () => {
26 | const Component = () => {
27 | useFirebaseApp();
28 | return null;
29 | };
30 | expect(() => render()).toThrow();
31 | });
32 |
33 | test('returns an app existing object if found', () => {
34 | let app;
35 | const Component = () => {
36 | app = useFirebaseApp();
37 | return null;
38 | };
39 | render(
40 |
41 |
42 |
43 | );
44 |
45 | expect(app.name).toBe('foo');
46 | });
47 |
48 | test('creates new app existing object if not found', () => {
49 | let app;
50 | const Component = () => {
51 | app = useFirebaseApp();
52 | return null;
53 | };
54 | render(
55 |
56 |
57 |
58 | );
59 |
60 | expect(app.options).toEqual({ bar: 'bar' });
61 | expect(app.name).toBe('[DEFAULT]');
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | import 'jest-dom/extend-expect';
2 |
3 | import * as index from '../src';
4 | import { FirebaseAppProvider, useFirebaseApp } from '../src/firebaseApp';
5 |
6 | describe('index', () => {
7 | test('exports FirebaseAppProvider and useFirebaseApp', () => {
8 | expect(index.FirebaseAppProvider).toBe(FirebaseAppProvider);
9 | expect(index.useFirebaseApp).toBe(useFirebaseApp);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/__tests__/useFirebase.test.js:
--------------------------------------------------------------------------------
1 | import { testHook, cleanup } from 'react-testing-library';
2 | import 'jest-dom/extend-expect';
3 |
4 | import useFirebase from '../src/useFirebase';
5 |
6 | afterEach(cleanup);
7 |
8 | describe('useFirebase', () => {
9 | test('returns an existing app if found by name', () => {
10 | const mockFirebase = {
11 | apps: [
12 | { name: 'baz' },
13 | { name: 'foo' },
14 | ],
15 | };
16 | const config = 'config';
17 | let app;
18 | testHook(() => {
19 | app = useFirebase(mockFirebase, config, 'foo');
20 | });
21 | expect(app).toEqual({ name: 'foo' });
22 | });
23 |
24 | test('returns a new app if not found', () => {
25 | const mockFirebase = {
26 | initializeApp: config => config,
27 | apps: [
28 | { name: 'foo' },
29 | ],
30 | };
31 | const config = { name: 'foo2' };
32 | let app;
33 | testHook(() => {
34 | app = useFirebase(mockFirebase, config, 'foo2');
35 | });
36 | expect(app).toEqual({ name: 'foo2' });
37 | });
38 |
39 | // test('name defaults to `[DEFAULT]`', () => {
40 | // const mockFirebase = {
41 | // initializeApp: config => config,
42 | // apps: [
43 | // { name: '[DEFAULT]' },
44 | // ],
45 | // };
46 | // const config = { name: 'foo2' };
47 | // let app;
48 | // testHook(() => {
49 | // app = useFirebase(mockFirebase, config);
50 | // });
51 | // expect(app).toEqual({ name: '[DEFAULT]' });
52 | // });
53 |
54 | test('if no parameter change, returns the same app', () => {
55 | const mockFirebase = {
56 | initializeApp: config => config,
57 | apps: [
58 | { name: 'foo' },
59 | ],
60 | };
61 | const config = { name: 'foo2' };
62 | let app;
63 | const { rerender } = testHook(() => {
64 | app = useFirebase(mockFirebase, config, 'foo2');
65 | });
66 |
67 | const savedApp = app;
68 |
69 | rerender();
70 |
71 | expect(app).toBe(savedApp);
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@use-firebase/app",
3 | "publishConfig": {
4 | "access": "public"
5 | },
6 | "version": "0.3.3",
7 | "description": "A custom React Hook that provides a declarative interface for Firebase.",
8 | "main": "dist/app.js",
9 | "umd:main": "dist/app.umd.js",
10 | "module": "dist/app.m.js",
11 | "source": "src/index.js",
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/donavon/use-firebase-app.git"
16 | },
17 | "scripts": {
18 | "prepublishOnly": "npm run build",
19 | "lint": "eslint src",
20 | "test": "jest --verbose --coverage --silent",
21 | "test:watch": "jest --watch --runInBand --silent",
22 | "prebuild": "npm run lint && npm t && rimraf dist",
23 | "build": "microbundle -o dist/ --sourcemap false --target web --jsx React.createElement",
24 | "dev": "microbundle watch -o dist/ --sourcemap false --compress false --jsx React.createElement"
25 | },
26 | "keywords": [
27 | "react-hooks",
28 | "hooks",
29 | "react",
30 | "utils",
31 | "lib",
32 | "firebase"
33 | ],
34 | "author": "Donavon West (https://github.com/donavon)",
35 | "devDependencies": {
36 | "@babel/core": "^7.3.4",
37 | "@babel/preset-env": "^7.3.4",
38 | "@babel/preset-react": "^7.0.0",
39 | "babel-core": "^7.0.0-bridge.0",
40 | "babel-jest": "^23.6.0",
41 | "eslint": "^5.15.1",
42 | "eslint-config-amex": "^9.0.0",
43 | "jest": "^23.6.0",
44 | "jest-dom": "^3.0.0",
45 | "microbundle": "^0.9.0",
46 | "react": "^16.8.3",
47 | "react-dom": "^16.8.3",
48 | "react-testing-library": "^5.9.0",
49 | "rimraf": "^2.6.2"
50 | },
51 | "peerDependencies": {
52 | "react": "^16.8.0"
53 | },
54 | "jest": {
55 | "coverageThreshold": {
56 | "global": {
57 | "branches": 100,
58 | "functions": 100,
59 | "lines": 100,
60 | "statements": 100
61 | }
62 | }
63 | },
64 | "dependencies": {
65 | "firebase": "^7.19.1",
66 | "prop-types": "^15.7.2"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/firebaseApp.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useMemo, useDebugValue } from 'react';
2 | import PropTypes from 'prop-types';
3 | import _firebase from 'firebase/app';
4 |
5 | import useFirebase from './useFirebase';
6 |
7 | const FirebaseContext = React.createContext();
8 | const providerSignature = Symbol();
9 |
10 | const FirebaseAppProvider = ({
11 | firebase, config, name, children,
12 | }) => {
13 | const app = useFirebase(firebase, config, name);
14 | const payload = useMemo(() => ({ signature: providerSignature, app }), [app]);
15 |
16 | return (
17 |
18 | {children}
19 |
20 | );
21 | };
22 | FirebaseAppProvider.propTypes = {
23 | firebase: PropTypes.shape({
24 | apps: PropTypes.arrayOf(PropTypes.shape({
25 | name: PropTypes.string.isRequired,
26 | })),
27 | initializeApp: PropTypes.func,
28 | }),
29 | config: PropTypes.shape({
30 | apiKey: PropTypes.string.isRequired,
31 | authDomain: PropTypes.string.isRequired,
32 | projectId: PropTypes.string.isRequired,
33 | }).isRequired,
34 | name: PropTypes.string,
35 | children: PropTypes.element.isRequired,
36 | };
37 | FirebaseAppProvider.defaultProps = {
38 | firebase: _firebase,
39 | name: '[DEFAULT]',
40 | };
41 |
42 | const useFirebaseApp = () => {
43 | const { signature, app } = useContext(FirebaseContext) || {};
44 |
45 | if (signature !== providerSignature) {
46 | throw new Error(
47 | 'useFirebaseApp must be a descendant of '
48 | );
49 | }
50 | useDebugValue(app.name);
51 | return app;
52 | };
53 |
54 | export { FirebaseAppProvider, useFirebaseApp };
55 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | // import firebase from 'firebase/app';
2 | // import _useFirebase from './useFirebase';
3 |
4 | // const useFirebase = (...args) => _useFirebase(firebase, ...args);
5 |
6 | // // eslint-disable-next-line import/prefer-default-export
7 | // export { useFirebase };
8 |
9 | export * from './firebaseApp';
10 |
--------------------------------------------------------------------------------
/src/useFirebase.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 |
3 | const getApp = (firebase, name, config) => firebase.apps.find(a => a.name === name)
4 | || firebase.initializeApp(config, name);
5 |
6 | const useFirebase = (firebase, config, name) => {
7 | const [app, setApp] = useState(() => getApp(firebase, name, config));
8 |
9 | useEffect(() => {
10 | setApp(() => getApp(firebase, name, config));
11 | }, [firebase, name, config]);
12 |
13 | return app;
14 | };
15 |
16 | export default useFirebase;
17 |
--------------------------------------------------------------------------------