UNPKG

@taraai/read-write

Version:

Synchronous NoSQL/Firestore for React

129 lines (115 loc) 4.12 kB
import { merge } from 'lodash/fp'; import { firestoreActions } from './actions'; import { mapWithFirebaseAndDispatch } from './utils/actions'; import { defaultConfig, methodsToAddFromFirestore } from './constants'; let firestoreInstance; /** * Create a firebase instance that has helpers attached for dispatching actions * @param {object} firebase - Firebase instance which to extend * @param {object} configs - Configuration object * @param {Function} dispatch - Action dispatch function * @returns {object} Extended Firebase instance */ export default function createFirestoreInstance(firebase, configs, dispatch) { // Setup internal variables const defaultInternals = { // Setup empty listeners object (later used to track listeners) listeners: {}, // Setup empty path listeners count object (later used to track listeners) pathListenerCounts: {}, // Extend default config with provided config config: { ...defaultConfig, ...configs }, }; // console.log(firebase); // extend existing firebase internals (using redux-firestore along with redux-firebase) // firebase._ = merge(defaultInternals, firebase._); // eslint-disable-line no-param-reassign // Aliases for methods const aliases = [ { action: firestoreActions.deleteRef, name: 'delete' }, { action: firestoreActions.setListener, name: 'onSnapshot' }, ]; // Create methods with internal firebase object and dispatch passed const methods = mapWithFirebaseAndDispatch( firebase, dispatch, firestoreActions, aliases, ); // Only include specific methods from Firestore since other methods // are extended (list in constants) // const firestore = firebase.firestore(); const methodsFromFirestore = methodsToAddFromFirestore.reduce( (acc, methodName) => typeof firebase.firestore()[methodName] === 'function' ? { ...acc, // [methodName]: firestore[methodName].bind(firestore), // -- TODO: memory leak seems to be here [methodName]: function () { const fs = firebase.firestore(); return fs[methodName].call(fs, ...arguments); }, } : acc, {}, ); methodsFromFirestore.mutate = methods.mutate; firestoreInstance = Object.assign( methodsFromFirestore, firebase.firestore, { _: firebase._ }, configs.helpersNamespace ? // Attach helpers to specified namespace { [configs.helpersNamespace]: methods } : methods, ); if (location && location.hostName === 'localhost') { console.log(`🔥 Read/Write started. Enable logging: localStore.debug = 'readwrite:cache,readwrite:mutate'; See stats for document loading phases: readwriteStats(true)`); } return firestoreInstance; } /** * Expose Firestore instance created internally. Useful for * integrations into external libraries such as redux-thunk and redux-observable. * @returns {object} Firebase Instance * @example <caption>redux-thunk integration</caption> * import { applyMiddleware, compose, createStore } from 'redux'; * import thunk from 'redux-thunk'; * import makeRootReducer from './reducers'; * import { reduxFirestore, getFirestore } from 'redux-firestore'; * * const fbConfig = {} // your firebase config * * const store = createStore( * makeRootReducer(), * initialState, * compose( * applyMiddleware([ * // Pass getFirestore function as extra argument * thunk.withExtraArgument(getFirestore) * ]), * reduxFirestore(fbConfig) * ) * ); * // then later * export const addTodo = (newTodo) => * (dispatch, getState, getFirestore) => { * const firestore = getFirestore() * firestore * .add('todos', newTodo) * .then(() => { * dispatch({ type: 'SOME_ACTION' }) * }) * }; */ export function getFirestore() { /* istanbul ignore next: Firestore instance always exists during tests */ if (!firestoreInstance) { throw new Error('Firestore instance does not yet exist. Check your setup.'); } return firestoreInstance; }