I am trying to fetch some data from Firebase.
Where should be the reference to Firebase
_var Firebase = require('firebase/lib/firebase-web')_
_const ref = new Firebase("https://some-app.firebaseio.com")_
and where should be the data retrieved?
Thanks for any help!
This is just one way of doing it:
I store the Firebase ref in the Redux store. I then attach a value listener that continually mirrors the Firebase data into a redux reducer. In the actions, I use redux-thunk's getState to access the ref and do any modifications to the data.
I like this way of doing it because:
chatRooms base ref, each chat room might need to append a suffix on the base ref when listening for messages in the room.An example:
// actions/config.js
/**
* Called every time the firebase ref changes
*/
function replaceConfig(config) {
return {
type: 'CONFIG_REPLACE',
value: config
};
}
/**
* Start listening to changes when the app boots
*/
function listenToConfigChanges() {
return (dispatch, getState) => {
const { firebaseRef } = getState();
firebaseRef.child('config').on('value', (snapshot) => {
dispatch(replaceConfig(snapshot.val()));
});
};
}
/*
* Save new config data
*/
function saveConfig(config) {
return (dispatch, getState) => {
const { firebaseRef } = getState();
firebaseRef.child('config').set(config);
// no need for dispatch, it will trigger Firebase 'value', which will dispatch `replaceConfig`
};
}
// reducers/config.js
const initialState = {};
function config(state = initialState, action) {
switch (action.type) {
case 'CONFIG_REPLACE':
return Object.assign({}, action.value); // note: we replace state entirely here
default:
return state;
}
}
// actions/firebaseRef.js
function setFirebaseRef(ref) {
return {
type: 'FIREBASE_REF_SET',
value: ref
};
}
// reducers/firebaseRef.js
const initialState = null;
function firebaseRef(state = initialState, action) {
switch (action.type) {
case 'FIREBASE_REF_SET':
return action.value;
default:
return state;
}
}
Then, on app boot, set the Firebase ref and start listening to changes:
var config = {}; // this will come from your environment specific config
var ref = new Firebase(config.firebaseRef);
var actions = bindActionCreators(/* ... */);
actions.setFirebaseRef(ref);
An on the config panel:
// start listening somewhere
this.props.actions.listenToConfigChanges();
// to save some data
this.props.actions.saveConfig({
new: 'config'
});
Note: I used on('value') listeners in this example for brevity. You would modify the example to use the child_added/child_changed/child_removed events if you were dealing with a collection of things.
Thanks for fielding this so beautifully, @tstirrat.
Absolutely @erikras :+1: Many thanks for your comprehensive answer @tstirrat I will dig into that. So that I won't need the projects api in this case?
I added the
actions.setFirebaseRef(ref);
to
componentWillMount()
to App.js container, but I'm getting the "ROUTER ERROR: TypeError: Converting circular structure to JSON" error. I tried the cycle.js, but when I decycle the 'ref', it goes decycled to the store. Is this good approach, maybe somewhere to retrocycle the 'ref' before adding it to the redux store?
Thanks a lot!
The ref will not serialize to JSON very well. I didn't have this problem, but I wasn't doing hot loading or state fast forwarding etc.
I wouldn't try to de-cycle the ref, it will probably introduce subtle bugs (It will remove the ref's root reference). Another approach is to store just url in the store, then reconstitute the Firebase ref when you need to act upon it:
// actions/firebaseRef.js
function setFirebaseRef(url) {
return {
type: 'FIREBASE_REF_SET',
value: url
};
}
In each of your actions just create the ref when needed:
// actions/config.js
import Firebase from 'firebase';
// ...
/**
* Start listening to changes when the app boots
*/
function listenToConfigChanges() {
return (dispatch, getState) => {
const { firebaseRef } = getState();
var ref = new Firebase(firebaseRef);
ref.child('config').on('value', (snapshot) => {
dispatch(replaceConfig(snapshot.val()));
});
};
}
/*
* Save new config data
*/
function saveConfig(config) {
return (dispatch, getState) => {
const { firebaseRef } = getState();
var ref = new Firebase(firebaseRef);
ref.child('config').set(config);
}
The Firebase refs are singletons, as long as the url is the same, the refs will be equivalent. e.g.
var ref1 = new Firebase('https://blah-demo.firebaseio.com');
var ref2 = new Firebase('https://blah-demo.firebaseio.com');
var handler = function () {};
ref1.on('value', handler);
ref2.off('value', handler); // this is fine
Shall we close? @erikras
We shall.
Would we need specific middleware to be able to load data server-side? How would we send back the response on the value event?
Most helpful comment
This is just one way of doing it:
I store the Firebase
refin the Redux store. I then attach avaluelistener that continually mirrors the Firebase data into a redux reducer. In the actions, I useredux-thunk'sgetStateto access the ref and do any modifications to the data.I like this way of doing it because:
chatRoomsbase ref, each chat room might need to append a suffix on the base ref when listening for messages in the room.An example:
Then, on app boot, set the Firebase ref and start listening to changes:
An on the config panel:
Note: I used
on('value')listeners in this example for brevity. You would modify the example to use thechild_added/child_changed/child_removedevents if you were dealing with a collection of things.