Skip to content

Instantly share code, notes, and snippets.

@gaboelnuevo
Last active May 1, 2018 03:58
Show Gist options
  • Save gaboelnuevo/d435c712dc34a83242ef982b76e53b2c to your computer and use it in GitHub Desktop.
Save gaboelnuevo/d435c712dc34a83242ef982b76e53b2c to your computer and use it in GitHub Desktop.
React.js Observable Props
import React from 'react';
import Services from './services';
import { combineLatest } from 'rxjs/observable/combineLatest';
import { Observable } from 'rxjs/Observable';
import "rxjs/add/operator/switchMap";
import 'rxjs/add/observable/from';
import * as _ from 'lodash';
export function servicesConnector(WrappedComponent, mergeProps=null) {
return class ServicesConnector extends React.Component {
static navigationOptions = WrappedComponent.navigationOptions || {};
constructor(props) {
super(props);
this.Services = Services;
}
render() {
let mergedProps = mergeProps ? mergeProps(this.Services, this.props) : {
...this.props,
Services: this.Services
};
return (
// that renders your component
<WrappedComponent {...mergedProps}/>
)
}
}
}
export function addNavigationOptions(WrappedComponent, navigationOptions) {
return class ScreenWithNavigationOptions extends React.Component {
static navigationOptions = navigationOptions || {};
render() {
return (
// that renders your component
<WrappedComponent {...this.props}/>
)
}
}
}
export function watchObservableProps(WrappedComponent) {
return class ObservableWatcher extends React.Component {
state = {
loading: true,
observedProps: {},
}
getObservableKeys(props) {
return Object.keys(props).filter(key => {
return props[key] && props[key] instanceof Observable;
});
}
componentWillReceiveProps(nextProps) {
this.teardownSubscription();
this.setupSubscription(nextProps);
}
_combineObservablesFrom = (props) => {
let observablePropKeys = this.getObservableKeys(props);
let observables = observablePropKeys.map(key => props[key]);
return observables.length >= 1 ? combineLatest(...observables) : Observable.from([{}]);
}
mapObservedProps(observedProps, props){
let observablePropKeys = this.getObservableKeys(props);
return _.assign({}, ...observablePropKeys.map((key, index) => {
let _obj = {};
_obj[key] = observedProps[index];
return _obj;
}));
}
setupSubscription(props){
this.subscription = this._combineObservablesFrom(props).subscribe(observedProps => {
this.setState({
loading: false,
observedProps: this.mapObservedProps(observedProps, props),
});
});
}
teardownSubscription() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
componentWillMount() {
this.setupSubscription(this.props);
}
componentWillUnmount(){
this.teardownSubscription();
}
render() {
const { loading, observedProps } = this.state;
if (loading) return null;
return (
// that renders your component
<WrappedComponent
{...{...this.props, ...observedProps}}
/>
)
}
}
}
import React from 'react';
import {
servicesConnector,
addNavigationOptions,
watchObservableProps,
} from './connectors';
import { realmResultToObservable } from './Utils';
import { Observable } from 'rxjs/Observable';
import "rxjs/add/operator/map";
import "rxjs/add/operator/switchMap";
import "rxjs/add/operator/startWith";
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/observable/fromPromise';
class MyScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: "MyScreen",
}
};
/* ... */
render() { /* ... */ }
}
const mergeProps = (Services, { navigation, ...otherProps }) => {
const Realm = Services.getRealm();
let items = Realm.objects('MyObject').filtered("enabled = true");
return {
navigation,
Services,
imtems: realmResultToObservable(items),
itemsCounter: realmResultToObservable(items).map((results) => results.length),
};
}
const MyConnectedScreen = servicesConnector(watchObservableProps(MyScreen), mergeProps);
// add navigationOptions for react-navigation
export default addNavigationOptions(MyConnectedScreen, MyScreen.navigationOptions);
export default {
/* Add custom services */
}
import { Observable } from 'rxjs/Observable';
import * as _ from 'lodash';
// use with realm.io
export const realmResultToObservable = (query) => {
return Observable.create(function (observer) {
let _onChanges = (collection, changes) => {
observer.next(query);
}
_onChanges = _.debounce(_onChanges, 100, { leading: true, trailing: true });
observer.next(query);
query.addListener(_onChanges);
return function () {
query.removeListener(_onChanges);
};
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment