├── .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 | [![npm version](https://badge.fury.io/js/%40use-firebase%2Fapp.svg)](https://badge.fury.io/js/%40use-firebase%2Fapp) 6 | [![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#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 | --------------------------------------------------------------------------------