Skip to content

Instantly share code, notes, and snippets.

@scott-coates
Last active August 23, 2017 23:44
Show Gist options
  • Save scott-coates/d53c9de94147eae5f7c6 to your computer and use it in GitHub Desktop.
Save scott-coates/d53c9de94147eae5f7c6 to your computer and use it in GitHub Desktop.
Imperative vs FRP code. 'Subscribe' to a Redux Store, only receiving events when a specific piece of state is changed.
const teamStream = storeObserver.observeStateStream(store, state => state.team.requestedTeam.id);
teamStream.onValue(requestedTeamId => {
// clean up old connection (this will happen if we look at different teams)
if (this.teamRef) this.teamRef.off('value', this.teamCallback);
this.teamRef = rootRef.child(`teams/${requestedTeamId}`);
this.teamCallback = this.teamRef.on('value', snapshot => {
try {
const team = firebaseService.prepareObject(snapshot);
if (team.executionDate) team.executionDate = ymdFormat(dateFromTimestamp(team.executionDate));
store.dispatch(teamActions.teamReceived(team));
} catch (error) {
throw new Error(`Error providing team data from firebase: Inner exception: ${error.stack}`);
}
}, error => {
throw new Error(`Error retrieving team data from firebase: Inner exception: ${error.stack}`);
});
});
store.subscribe(_ => {
const state = store.getState();
// we need to do this the very first time (requestedTeamId will be null).
const requestedTeamId = state.team.requestedTeam.id;
if (requestedTeamId) {
if (this.currentRequestedTeam.id !== requestedTeamId) {
// clean up old connection (this will happen if we look at different team)
if (this.teamRef) this.teamRef.off('value', this.teamCallback);
this.currentRequestedTeam.id = requestedTeamId;
this.teamRef = rootRef.child(`teams/${this.currentRequestedTeam.id}`);
this.teamCallback = this.teamRef.on('value', snapshot => {
try {
const team = FirebaseService.prepareObject(snapshot);
store.dispatch(teamActions.teamReceived(team));
} catch (error) {
throw new Error(`Error providing team data from firebase: Inner exception: ${error.stack}`);
}
}, error => {
throw new Error(`Error retrieving team data from firebase: Inner exception: ${error.stack}`);
});
}
}
}
import bacon from 'baconjs';
import equals from 'deep-equal';
export default {
observeStateStream(store, mapFunc){
const storeBinderFunc = sink => {
store.subscribe(_=>sink(store.getState()));
};
const stream = bacon
.fromBinder(storeBinderFunc)
// get the initial state now so we have something to compare against on the next event.
.startWith(store.getState())
.map(mapFunc)
.skipDuplicates(equals)
// do not log first time
.skip(1);
return stream;
}
};
// const TeamStream = storeObserver.observeStateStream(store, state=> state.team.requestedTeam.id);
// teamStream.onValue(console.log.bind(console, "New team ID is"));
// Console Output: New team ID is bF8fLMXn
@scott-coates
Copy link
Author

This snippet is inspired from http://stackoverflow.com/questions/31268740/firing-redux-actions-in-response-to-route-transitions-in-react-router/31312139#31312139.

Note: store.subscribe is invoked anytime its internal state changes.

before.js

before.js shows an imperative approach. We're keeping track of store's state (particularly requestedTeamId) with a temp variable currentRequestedTeam. We use this temp variable to detect when requestedTeamId changes. It uses a bunch of conditionals and temp variables - I've found that these are often scenarios in which FRP can come in handy.

after.js

This approach allows us to specify which piece of state we care about and only when it changes will an event be fired.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment