src/index.js
import { eventChannel } from 'redux-saga';
import { put, fork, call, take } from 'redux-saga/effects';
const EVENT_TYPES = ['child_added', 'child_removed'];
function newOpts(name = 'data') {
const opts = {};
const chan = eventChannel(emit => {
opts.handler = obj => {
emit({ [name]: obj });
};
return () => {};
});
chan.handler = opts.handler;
return chan;
}
function newKey(path) {
return firebase.database().ref().child(path).push().key;
}
/**
* Fetches a record specified by the key from the database
*
* @param path
* @param key
* @returns {*|any}
* import { get } from 'firebase-saga';
*
* const posts = yield call(get, 'posts', '1');
*/
export function* get(path, key) {
const ref = firebase.database().ref(`${path}/${key}`);
const data = yield call([ref, ref.once], 'value');
return data.val();
}
/**
* Fetches entire snapshot of the database
*
* @param path
* @returns {*|any}
* @example
* import { getAll } from 'firebase-saga';
*
* const posts = yield call(getAll, 'posts');
*/
export function* getAll(path) {
const ref = firebase.database().ref(path);
const data = yield call([ref, ref.once], 'value');
return data.val();
}
export function* create(path, fn) {
const key = yield call(newKey, path);
const payload = yield call(fn, key);
const opts = newOpts('error');
const ref = firebase.database().ref();
const [ _, { error } ] = yield [
call([ref, ref.update], payload, opts.handler),
take(opts)
];
return error;
}
export function* update(path, key, payload) {
if (typeof payload === 'function') {
payload = yield call(payload);
}
const opts = newOpts('error');
const ref = firebase.database().ref(`${path}/${key}`);
const [ _, { error } ] = yield [
call([ref, ref.update], payload, opts.handler),
take(opts)
];
return error;
}
function* runSync(ref, eventType, creator) {
const opts = newOpts();
yield call([ref, ref.on], eventType, opts.handler);
while (true) {
const { data } = yield take(opts);
yield put(creator({ data }));
}
}
export function* sync(path, mapEventToAction = {}, limit = 20) {
const ref = firebase.database().ref(path).limitToLast(limit);
for (let type of EVENT_TYPES) {
const action = mapEventToAction[type];
if (typeof action === 'function') {
yield fork(runSync, ref, type, action);
}
}
}